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

下面的代码被 GCC 和 Clang 用 -std=c++14 愉快地接受,但导致 Visual Studio 2013 编译错误.

The code below is happily accepted by both GCC and Clang with -std=c++14 but causes a compile error with 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;
}

错误是:

main.cpp(11): error C2446: ':': no conversion from 'main::<lambda_0228ee097b83254cfd8aa5f4015a245b>''main::<lambda_cb3b816d067baa9d4462132ee332363c>'main.cpp(11): 注意:没有可用的用户定义的转换运算符可以执行此转换,或者无法调用该运算符

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

I guess my question is which compiler is compliant here, I am guessing that it is not MSVC, and is there a part of the standard which explicitly deals with this situation?

推荐答案

由于 lambda 捕获它们都可以转换为具有兼容签名的函数指针,所以 gccclang在这里是正确的.

Since neither lambda capture they can be converted to function pointers with compatible signatures, so gcc and clang are correct here.

有一个 gcc 错误报告很好地总结了这个主题: [c++ lambda]通过operator?:"分配 lambda expr 时出错,同时捕获 涵盖了这一点并说:

There is a gcc bug report which summarizes this topic well: [c++ lambda] error in assigning lambda expr though "operator?:" while capturing that covers this and says:

编译器的行为在我看来是正确的.的区别bar 和 foo3 中的 lambda 表达式与其他两个相比的是这些是无捕获的 lambda,因此具有转换功能函数指针.

The compiler behaviour looks correct to me. The difference of the lambda expressions in bar and foo3 compared to the other two is that these are capture-free lambdas and thus have a conversion function to function pointer.

每个 lambda 表达式对应一个唯一的类类型,所以我们foo1foo2 中的 have 可以与以下进行比较类示例:

Each lambda expression corresponds to a unique class type, so what we have in foo1 and foo2 can be compared with the following class-example:

struct A{}; struct B{};
void f() { false ? A() : B(); }

这个表达式没有条件运算符的通用类型,并且是格式错误.

This expression has no common type for the conditional operator and is ill-formed.

我们在 barfoo3 中的内容可以与以下内容进行比较类示例:

What we have in bar and foo3 can be compared with the following class-example :

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

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

void f() { false ? A() : B(); }

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

This is well-formed, because in the last step of the conditional operator conversion attempts (5.16p5), more general conversions are attempted and these find the common pointer to function.

5.16p5 说:

否则,结果是一个纯右值.如果第二个和第三个操作数没有相同的类型,并且有(可能是 cv 限定的)类类型,重载决议用于确定转换(如果有)应用于操作数(13.3.1.2、13.6).如果重载解析失败,程序格式错误.否则,应用如此确定的转换,并且转换后的操作数用于代替原始操作数的其余部分部分.

Otherwise, the result is a prvalue. If the second and third operands do not have the same type, and either has (possibly cv-qualified) class type, overload resolution is used to determine the conversions (if any) to be applied to the operands (13.3.1.2, 13.6). If the overload resolution fails, the program is ill-formed. Otherwise, the conversions thus determined are applied, and the converted operands are used in place of the original operands for the remainder of this section.

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

If we change your code as follows:

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

gccclang 都会产生错误,clang 说 (现场观看):

both gcc and clang generate an error, clang says (see it live):

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);
                                      ^ ~~~~~~~~~~   ~~~~~~~~~~

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

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

The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

在 C++14 标准草案中修改了措辞,但没有改变这个属性.

The wording is modified in the draft C++14 standard but does not alter this property.

更新

提交了错误报告.

相关文章