传入 Lambda 时,Visual Studio 2017 中的哪些扩展可以消除“bool”与“std::function”的歧义?
Posted
技术标签:
【中文标题】传入 Lambda 时,Visual Studio 2017 中的哪些扩展可以消除“bool”与“std::function”的歧义?【英文标题】:What Extension In Visual Studio 2017 Disambiguates "bool" vs "std::function" When Passing In A Lambda? 【发布时间】:2017-08-09 05:06:23 【问题描述】:以下在 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 for iOS 中编译。据我所知,我无法在所有编译器中获得 Visual Studio 展示的相同行为(这会很好)。因此,我正在尝试编辑我的项目以使其更符合标准。
为了在 Visual Studio 中执行此操作,因为它是我的主要工作环境,我想知道正在使用哪个非标准扩展,以及如果可能的话如何禁用它。我可以尝试在 Xcode 中执行此操作,但是对于这个特殊问题,我发现了很多模棱两可的问题,而且一次只能给我一些。
作为一个额外的好奇心,我想知道这个模棱两可的案例是否有任何标准建议来解决它,或者 Visual Studio 在这种情况下是否完全是流氓。
【问题讨论】:
【参考方案1】:这是由两件事的相互作用引起的:
MSVC 的无捕获 lambda 具有 许多 到函数指针的转换函数 - 每个调用约定一个。因此,通过函数指针将无捕获 lambda 转换为 bool
在 MSVC 中是不明确的。
MSVC 没有将这种模棱两可的转换视为 模棱两可的转换序列,as the standard requires 将其视为根本没有转换序列,这反过来意味着 @987654323 @重载是不可行的。这使得 function
重载成为唯一可行的重载。
禁用转换为bool
的最简单方法是给 lambda 一个捕获或捕获默认值 - 即使它最终没有捕获任何内容,具有捕获默认值也足以禁用转换到函数指针。
【讨论】:
为什么这应该是模棱两可的? lambda 的实际调用不需要添加调用运算符(这在 OP 的情况下是非法的,因为它确实返回 void):functionTest([]()->bool std::cout << "wut"; return true; ());
-注意额外的 ()
@Swift 你误解了歧义。 lambda to function pointer to bool (aka test for null) 是转换序列; MSVC 有无数不同的函数指针中间类型。它与调用 lambda 或函数指针无关。
@Yakk 啊,因为我不认为这种转换是合法的。那不是仅在某些情况下适用的上下文转换。 MSVC at 没有将 lambdas 视为可转换为 bool 的类型,在其实现中没有定义 bool 运算符。函数得到了操作符,但 lambda 永远不会被强制转换为在此上下文中的函数
这是一个很好的答案,谢谢!我使用 Yakk 只是为了有用的 Exactly 模板。【参考方案2】:
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 污染函数签名。
【讨论】:
我将其标记为解决方案,即使其他答案更简洁,仅适用于第三种消歧方法,它允许在构造函数中使用完全以上是关于传入 Lambda 时,Visual Studio 2017 中的哪些扩展可以消除“bool”与“std::function”的歧义?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Roslyn 在尝试重写此 lambda 时会崩溃? (Visual Studio 2015 更新 1)
Visual Studio 2015 在 constexpr 中使用 lambda
“使用 Visual Studio 调试 lambda 表达式”不再起作用?
如何配置 Visual Studio Code 以解析 AWS Lambda 层的输入路径 (javascript)