为什么这不能编译:
int myVar = 0;
myVar ? []()->void{} : []()->void{};
以下错误消息:
错误2错误C2446:':':没有从'red_black_core ::`anonymous-namespace':: <lambda1>'转换为red_black_core :: anonymous-namespace :: <lambda0>
虽然这符合正确:
void left()
{}
void right()
{}
int myVar = 0;
myVar ? left() : right();
必须从它的两个操作数推导出?:运算符的返回类型,并且确定这种类型的规则非常复杂.Lambdas不满足他们,因为他们不能互相转换.那么当编译器试图弄清楚结果是什么时:?是,那么就没有结果,因为这两个lambda不能相互转换.
但是,当您尝试计算函数时,您实际上调用了它们,但是您没有调用 lambdas.因此,当您调用函数时,它们都具有void,因此返回类型?:为void.
这个
void left()
{}
void right()
{}
int myVar = 0;
myVar ? left() : right();
相当于
int myVar = 0;
myVar ? [](){}() : [](){}();
注意末尾的extra() - 我实际上称为lambda.
你原来的相当于
compiler_deduced_type var;
if (myVar)
    var = [](){};
else
    var = [](){};
但是 - 不存在任何可以同时为lambdas的类型.编译器完全有权使两个lambda都有不同的类型.
编辑:
我记得有些事.在最新的标准草案中,没有捕获的lambda可以隐式转换为相同签名的函数指针.也就是说,在上面的代码中,compiler_deduced_type可以是void(*)().但是,我知道MSVC不包含此行为这一事实,因为它们在实现lambdas时尚未定义.这可能是GCC允许它而MSVC不支持的原因--GCC的lambda支持比MSVC更新.
n3225草案中的条件运算符规则曾经说过
否则,结果是prvalue.如果第二个和第三个操作数不具有相同的类型,并且具有(可能是cv-quali fi ed)类类型,则使用重载决策来确定要应用于操作数的转换(如果有)(13.3.1.2,13.6) .如果重载决策失败,则程序格式错误.否则,应用如此确定的转换,并使用转换的操作数代替本节其余部分的原始操作数.
到目前为止,所有其他替代方案(例如,将一个转换为另一个操作数)都失败了,所以我们现在将执行该段落所说的内容.我们将应用的转换由重载决定通过转换a ? b : c为operator?(a, b, c)(对所谓的函数的虚函数调用)来确定.如果你看看想象operator?中的候选人是什么,你会发现(其中包括)
对于每个类型T,其中T是指针,指向成员的指针或作用域枚举类型,存在表单的候选运算符函数
Run Code Online (Sandbox Code Playgroud)T operator?(bool, T , T );
这包括一个T类型的候选人void(*)().这很重要,因为lambda表达式产生了一个可以转换为这种类型的类的对象.规范说
没有lambda-capture的lambda表达式的闭包类型有一个公共的非虚拟非显式const转换函数,用于指向具有与闭包类型的函数调用操作符相同的参数和返回类型的函数.此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果.
lambda表达式无法转换为列出的任何其他参数类型,这意味着重载解析成功,找到一个operator?并将lambda表达式转换为函数指针.然后,条件opreator部分的其余部分将照常进行,现在具有两个分支,用于具有相同类型的条件运算符.
这就是为什么你的第一个版本还可以,以及GCC为什么接受它.但是我真的不明白为什么你要显示第二个版本 - 正如其他人所解释的那样,它正在做一些不同的事情并且它的工作原理并不令人惊讶,而另一个则没有(在你的编译器上).下次,最好不要在问题中包含无用的代码.
| 归档时间: | 
 | 
| 查看次数: | 1025 次 | 
| 最近记录: |