为什么这不能编译:
int myVar = 0;
myVar ? []()->void{} : []()->void{};
以下错误消息:
错误2错误C2446:':':没有来自'red_black_core ::`anonymous-namespace'::<的转换lambda1>” to red_black_core :: anonymous-namespace ::< lambda0>
虽然这符合正确:
void left()
{}
void right()
{}
int myVar = 0;
myVar ? left() : right();
答案 0 :(得分:9)
?:运算符的返回类型必须从它的两个操作数中推导出来,确定这种类型的规则非常复杂。 Lambdas不满足他们,因为他们不能互相转换。那么当编译器试图弄清楚结果是什么时:?是,那么就不会有结果,因为这两个lambda不能相互转换。
但是,当您尝试计算函数时,实际上调用它们,但您没有调用 lambdas。因此,当您调用函数时,它们都具有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更新。答案 1 :(得分:5)
n3225草案中的条件运算符规则在某一点上说
否则,结果是prvalue。如果第二个和第三个操作数不具有相同的类型,则任何一个 具有(可能是cv-quali fi ed)类类型,重载决策用于确定转换(如果有) 适用于操作数(13.3.1.2,13.6)。如果重载决策失败,则程序格式错误。除此以外, 应用如此确定的转换,并使用转换的操作数代替原始操作数 本节其余部分的操作数。
到目前为止,所有其他替代方案(例如,将一个转换为另一个操作数)都失败了,所以我们现在将执行该段落所说的内容。我们将应用的转换由重载决策决定,方法是将a ? b : c
转换为operator?(a, b, c)
(对所谓的函数进行虚函数调用)。如果你看看假想operator?
的候选人是什么,你会找到(其中包括)
对于每个类型T,其中T是指针,指向成员的指针或作用域枚举类型,存在表单的候选运算符函数
T operator?(bool, T , T );
这包括T
类型为void(*)()
的候选人。这很重要,因为lambda表达式产生了一个可以转换为这种类型的类的对象。规范说
没有lambda-capture的lambda表达式的闭包类型有一个公共的非虚拟非显式const转换函数,用于指向具有与闭包类型的函数调用操作符相同的参数和返回类型的函数。此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用操作符具有相同的效果。
lambda表达式无法转换为列出的任何其他参数类型,这意味着重载解析成功,找到单个operator?
并将两个lambda表达式转换为函数指针。然后,条件opreator部分的其余部分将照常进行,现在具有两个分支,用于具有相同类型的条件运算符。
这就是为什么你的第一个版本还可以,以及GCC为什么接受它。但是我真的不明白为什么你要显示第二个版本 - 正如其他人所解释的那样,它正在做一些不同的事情并且它的工作原理并不令人惊讶,而另一个则没有(在你的编译器上)。下次,最好不要在问题中包含无用的代码。
答案 2 :(得分:3)
因为每个lambda都是唯一的类型。它基本上是一个仿函数的语法糖,两个单独实现的仿函数不是同一类型,即使它们包含相同的代码。
标准确实指定如果lambdas没有捕获任何东西就可以转换为函数指针,但是在实现了MSVC的lambda支持之后添加了该规则。
但是,使用该规则,两个lambda可以转换为相同的类型,因此我相信您的代码对于兼容的编译器是有效的。
答案 3 :(得分:2)
两个片段都可以使用GCC 4.5.2进行编译。
也许你的编译器没有(或部分/破坏)支持C ++ 0x功能,比如lambda?
答案 4 :(得分:0)
编译失败。它工作得很好。 您的编译器可能没有启用C ++ 0x。
修改强> 现在已在原始问题中添加了错误消息!您似乎确实拥有C ++ 0x支持,但它在编译器中并不完整。这并不奇怪。
代码仍然是有效的C ++ 0x,但我建议你只需要使用C ++ 0x功能,直到它标准化并且在一系列工具链上有完全支持。你有一个可行的C ++ 03替代品,你在答案中给出了,我建议暂时使用它。
可能的替代解释:
另请注意,您可能没有写下您实际想写的内容。 []()->void{}
是一个lambda。 []()->void{}()
执行lambda并计算结果。根据您对此结果所做的操作,您的问题可能是调用lambda的结果为void
,而您对void
的帮助不大。