以下代码在 Visual Studio 2017 中使用 MSVC 编译器进行编译,但无法在 GCC 或 Clang 中编译。

#include <iostream>
#include <functional>

void functionTest(std::function<void()>) {
    std::cout << "F\n";
}

void functionTest(bool) {
    std::cout << "B\n";
}

int main() {
    functionTest([](){ std::cout << "wut"; });
}

为了解决这个问题,我们可以像这样使用 enable_if :
#include <iostream>
#include <functional>

void functionTest(std::function<void()>) {
    std::cout << "F\n";
}

template<typename BOOL_TYPE, typename = typename std::enable_if<std::is_same<bool, BOOL_TYPE>::value>::type>
void functionTest(BOOL_TYPE) {
    std::cout << "B\n";
}

int main() {
    functionTest([](){ std::cout << "wut"; });
}

或者我可以通过引入用户类型而不是 bool 来消除歧义(这是在构造函数存在歧义问题的情况下需要执行的操作):
#include <iostream>
#include <functional>

void functionTest(std::function<void()>) {
    std::cout << "F\n";
}

enum class DescriptiveTypeName {False, True};
void functionTest(DescriptiveTypeName) {
    std::cout << "B\n";
}

int main() {
    functionTest([](){ std::cout << "wut"; });
}

我在这里遇到的问题是我有一个非平凡大小的游戏项目,并且正在尝试在 Xcode 中为 iOS 进行编译。据我所知,我无法在所有编译器中获得 Visual Studio 展示的相同行为(这会很好)。因此,我试图编辑我的项目以使其更符合标准。

为了在 Visual Studio 中执行此操作,因为它是我的主要工作环境,我想知道正在使用哪个非标准扩展以及如何在可能的情况下禁用它。我可以尝试在 Xcode 中执行此操作,但是对于这个特定问题,我发现了很多模棱两可的问题,并且一次只能给我一些。

作为一个额外的好奇心,我想知道这个模棱两可的案例是否有任何标准建议来解决它,或者在这种情况下 Visual Studio 是否只是完全流氓。

最佳答案

lambda->bool 的转换其实就是 lambda->function pointer->bool。由于其中之一不符合“用户定义的转换”的条件,因此考虑双重转换。

在 MSVC 中,lambda 有多个 lambda-> 函数指针转换,每个调用约定一个。这不符合标准,其中函数指针没有附加到它们的调用约定类型。

在任何情况下,这都应该使 lambda->function pointer->bool 转换模棱两可(并触发错误),但是 MSVC 以某种方式决定将此歧义视为无效重载而不是错误,并选择没有歧义的那个。这似乎也违反了标准。

这两个标准违规一起产生了您想要的行为,这主要是偶然的。

我相信我们可以以符合标准的方式解决它,而不必在每个地方编写手动 SFINAE。这是一个尝试:

template<class T>
struct exactly {
  T t;
  template<class U, std::enable_if_t<std::is_same<T, std::decay_t<U>>{}, int> =0>
  exactly( U&& u ):t(std::forward<U>(u)) {}
  exactly():t() {}
  exactly(exactly&&)=default;
  exactly(exactly const&)=default;

  operator T() const& { return t; }
  operator T() && { return std::move(t); }
  T& get()& { return t; }
  T const& get() const& { return t; }
  T get()&& { return std::move(t); }
};

现在使用:
void functionTest(exactly<bool> b) {
  std::cout << "B\n";
}

live example

基本上我们将花哨的 SFINAE 移动到一个实用程序类中,从而避免 SFINAE 污染函数签名。

关于c++ - 当传入 Lambda 时,Visual Studio 2017 中的什么扩展可以消除 "bool"与 "std::function"的歧义?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45582048/

10-11 22:25
查看更多