使用无捕获的lambda表达式作为条件运算符的第二个和第三个操作数时出现MSVC错误

sjd*_*ing 5 c++ visual-c++ language-lawyer c++11 visual-studio-2013

下面的代码很高兴被GCC和Clang接受,-std=c++14但导致Visual Studio 2013出现编译错误.

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int main() {
    auto increasing = [](int lhs, int rhs){return lhs < rhs;};
    auto decreasing = [](int lhs, int rhs){return lhs > rhs;};
    std::vector<int> v(0, 10);
    bool increase = true;
    std::sort(v.begin(), v.end(), increase ? increasing : decreasing);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

错误是:

main.cpp(11): error C2446: ':': no conversion from 'main::<lambda_0228ee097b83254cfd8aa5f4015a245b>' to 'main::<lambda_cb3b816d067baa9d4462132ee332363c>' main.cpp(11): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

我想我的问题是哪个编译器在这里兼容,我猜它不是MSVC,是否有标准的一部分明确处理这种情况?

Sha*_*our 11

因为无论是捕获拉姆达他们可以转化为功能兼容的签名指针,所以gccclang正确位置.

有一个gcc bug报告很好地总结了这个主题:[c ++ lambda]错误分配lambda expr虽然"operator?:"但捕获时覆盖了这个并说:

编译器行为对我来说是正确的.bar中的lambda表达式foo3与其他两个的区别在于它们是无捕获的lambdas,因此具有函数指针的转换函数.

每个lambda表达式对应一个唯一的类类型,因此我们拥有foo1foo2可以与下面的类示例进行比较:

struct A{}; struct B{};
void f() { false ? A() : B(); }
Run Code Online (Sandbox Code Playgroud)

该表达式对于条件运算符没有通用类型,并且格式错误.

我们有什么bar,并foo3可以与下面的类例子来比较:

struct A
{
    typedef void (*F)();
    operator F();
};

struct B
{
    typedef void (*F)();
    operator F();
};

void f() { false ? A() : B(); }
Run Code Online (Sandbox Code Playgroud)

这是格式良好的,因为在条件运算符转换尝试的最后一步(5.16p5)中,尝试了更多的常规转换,并且这些转换找到了函数的公共指针.

5.16p5 说:

否则,结果是prvalue.如果第二个和第三个操作数不具有相同的类型,并且具有(可能是cv限定的)类类型,则使用重载决策来确定要应用于操作数的转换(如果有)(13.3.1.2,13.6) .如果重载决策失败,则程序格式错误.否则,应用如此确定的转换,并使用转换的操作数代替本节其余部分的原始操作数.

如果我们更改您的代码如下:

int x = 20 ;
auto increasing = [&x](int lhs, int rhs){return lhs < rhs;};
auto decreasing = [&x](int lhs, int rhs){return lhs > rhs;};
Run Code Online (Sandbox Code Playgroud)

两者gccclang生成错误,clang说(现场直播):

error: incompatible operand types ('(lambda at prog.cc:8:23)' and '(lambda at prog.cc:9:23)')
std::sort(v.begin(), v.end(), increase ? increasing : decreasing);
                                      ^ ~~~~~~~~~~   ~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

作为参考,C++ 11标准草案 5.1.2 [expr.prim.lambda]说:

没有lambda-capture的lambda表达式的闭包类型有一个公共的非虚拟非显式const转换函数,用于指向具有与闭包类型的函数调用操作符相同的参数和返回类型的函数.此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果.

措辞在C++ 14标准草案中进行了修改,但并未改变此属性.

更新

提交了一份错误报告.

  • @Windoze来自C++ 11"国际标准"文档:*"没有lambda-capture的lambda表达式的闭包类型有一个公共的非虚拟非显式const转换函数来指向函数[...] "* (2认同)