为什么这不会?兰巴函数编译?
为什么编译失败:
int myVar = 0;
myVar ? []()->void{} : []()->void{};
出现以下错误消息:
错误 2 错误 C2446: ':' : 没有从 'red_black_core::`anonymous-namespace'::< 进行转换λ1>'到 red_black_core::anonymous-namespace::< λ0>
虽然这正确地符合:
void left()
{}
void right()
{}
int myVar = 0;
myVar ? left() : right();
Why does this fail to compile:
int myVar = 0;
myVar ? []()->void{} : []()->void{};
with following error msg:
Error 2 error C2446: ':' : no conversion from 'red_black_core::`anonymous-namespace'::< lambda1>' to red_black_core::anonymous-namespace::< lambda0>
While this complies correctly:
void left()
{}
void right()
{}
int myVar = 0;
myVar ? left() : right();
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
?: 运算符的返回类型必须从它的两个操作数中推断出来,并且确定该类型的规则非常复杂。 Lambda 不能满足它们,因为它们无法相互转换。因此,当编译器尝试计算 ?: 的结果是什么时,就不可能有结果,因为这两个 lambda 不能相互转换。
但是,当您尝试计算函数时,您实际上调用了它们,但没有调用 lambda。所以当你调用这些函数时,它们都有void,所以?:的返回类型是void。
这
相当于
注意最后的额外 () - 我实际上称为 lambda。
您最初拥有的内容相当于
But- 不存在可以同时是 lambda 的类型。编译器完全有权使两个 lambda 成为不同的类型。
编辑:
我想起了一些事情。在最新的标准草案中,没有捕获的 lambda 可以隐式转换为相同签名的函数指针。也就是说,在上面的代码中,compiler_duced_type 可以是 void(*)()。然而,我知道 MSVC 不包含这种行为,因为在他们实现 lambda 时还没有定义这种行为。这可能就是 GCC 允许而 MSVC 不允许的原因 - GCC 的 lambda 支持比 MSVC 的支持要新得多。
The return type of the ?: operator has to be deduced from it's two operands, and the rules for determining this type are quite complex. Lambdas don't satisfy them because they can't be converted to each other. So when the compiler tries to work out what the result of that ?: is, then there can't be a result, because those two lambdas aren't convertible to each other.
However, when you try to compute the functions, then you actually called them, but you didn't call the lambdas. So when you call the functions, they both have void, so the return type of ?: is void.
This
is equivalent to
Note the extra () on the end- I actually called the lambda.
What you had originally is equivalent to
But- no type exists that can be both lambdas. The compiler is well within it's rights to make both lambdas different types.
EDIT:
I remembered something. In the latest Standard draft, lambdas with no captures can be implicitly converted into function pointers of the same signature. That is, in the above code, compiler_deduced_type could be void(*)(). However, I know for a fact that MSVC does not include this behaviour because that was not defined at the time that they implemented lambdas. This is likely why GCC allows it and MSVC does not- GCC's lambda support is substantially newer than MSVC's.
n3225 草案中条件运算符的规则曾提到过
到目前为止,所有其他替代方案(例如,将一个操作数转换为另一个操作数)都失败了,因此我们现在将按照该段落所述进行操作。我们将应用的转换由重载决策通过转换
a ? b : c
转换为operator?(a, b, c)
(对所谓函数的虚构函数调用)。如果你看看假想的运算符?
的候选者是什么,你会发现(除其他外)这包括
T
为类型的候选者>void(*)()
。这很重要,因为 lambda 表达式生成可以转换为此类类型的类对象。规范说lambda 表达式无法转换为列出的任何其他参数类型,这意味着重载解析成功,找到单个
operator?
并将两个 lambda 表达式转换为函数指针。条件运算符部分的其余部分将照常进行,现在具有相同类型的条件运算符的两个分支。这就是为什么你的第一个版本也可以,也是为什么 GCC 正确地接受它。然而,我真的不明白为什么你要显示第二个版本 - 正如其他人所解释的,它正在做一些不同的事情,并且它可以工作而另一个版本不能(在你的编译器上)也就不足为奇了。下次,最好不要在问题中包含无用的代码。
Rules for conditional operator in the draft n3225 says at one point
Up to that point, every other alternative (like, convert one to the other operand) failed, so we will now do what that paragraph says. The conversions we will apply are determined by overload resolution by transforming
a ? b : c
intooperator?(a, b, c)
(an imaginary function call to a so-named function). If you look what the candidates for the imaginaryoperator?
are, you find (among others)And this includes a candidate for which
T
is the typevoid(*)()
. This is important, because lambda expressions yield an object of a class that can be converted to such a type. The spec saysThe lambda expressions can't be convert to any other of the parameter types listed, which means overload resolution succeeds, finds a single
operator?
and will convert both lambda expressions to function pointers. The remainder of the conditional opreator section will then proceed as usual, now having two branches for the conditional operator having the same type.That's why also your first version is OK, and why GCC is right accepting it. However I don't really understand why you show the second version at all - as others explained, it's doing something different and it's not surprising that it works while the other doesn't (on your compiler). Next time, best try not to include useless code into the question.
因为每个 lambda 都是唯一的类型。它基本上是函子的语法糖,两个单独实现的函子不是同一类型,即使它们包含相同的代码。
该标准确实指定,如果 lambda 不捕获任何内容,则可以将其转换为函数指针,但该规则是在实现 MSVC 的 lambda 支持后添加的。
但是,根据该规则,两个 lambda 可以转换为相同类型,因此我相信您的代码对于兼容的编译器来说是有效的。
Because every lambda is a unique type. It is basically syntactic sugar for a functor, and two separately implemented functors aren't the same type, even if they contain identical code.
The standard does specify that lambdas can be converted to function pointers if they don't capture anything, but that rule was added after MSVC's lambda support was implemented.
With that rule, however, two lambdas can be converted to the same type, and so I believe your code would be valid with a compliant compiler.
这两个代码片段都可以在 GCC 4.5.2 上编译得很好。
也许您的编译器没有(或部分/损坏)支持 C++0x 功能,例如 lambda?
Both snippets compile just fine with GCC 4.5.2.
Maybe your compiler has no (or partial/broken) support to C++0x features such as lambda?
它不会编译失败。它工作得很好。
您的编译器中可能没有启用 C++0x。编辑:
现在已将错误消息添加到原始问题中!看来您确实有 C++0x 支持,但它在您的编译器中并不完整。这并不奇怪。
该代码仍然是有效的 C++0x,但我建议仅在确实需要时才使用 C++0x 功能,直到它实现标准化并且得到一系列工具链的全面支持。您在答案中给出了一个可行的 C++03 替代方案,我建议暂时使用它。
可能的替代解释:
另请注意,您可能没有写出您真正想要写的内容。
[]()->void{}
是一个 lambda。[]()->void{}()
执行 lambda 并计算其结果。根据您对此结果执行的操作,您的问题可能是调用 lambda 的结果是void
,并且您无法使用void
做太多事情。It doesn't fail to compile. It works just fine.
You probably don't have C++0x enabled in your compiler.Edit:
An error message has now been added to the original question! It seems that you do have C++0x support, but that it is not complete in your compiler. This is not surprising.
The code is still valid C++0x, but I recommend only using C++0x features when you really have to, until it's standardised and there is full support across a range of toolchains. You have a viable C++03 alternative that you gave in your answer, and I suggest using it for the time being.
Possible alternative explanation:
Also note that you probably didn't write what you actually meant to write.
[]()->void{}
is a lambda.[]()->void{}()
executes the lambda and evaluates to its result. Depending what you're doing with this result, your problem could be that the result of calling your lambda isvoid
, and you can't do much withvoid
.