为什么这不会?兰巴函数编译?

发布于 2024-10-12 04:38:09 字数 401 浏览 0 评论 0原文

为什么编译失败:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

苏别ゝ 2024-10-19 04:38:09

?: 运算符的返回类型必须从它的两个操作数中推断出来,并且确定该类型的规则非常复杂。 Lambda 不能满足它们,因为它们无法相互转换。因此,当编译器尝试计算 ?: 的结果是什么时,就不可能有结果,因为这两个 lambda 不能相互转换。

但是,当您尝试计算函数时,您实际上调用了它们,但没有调用 lambda。所以当你调用这些函数时,它们都有void,所以?:的返回类型是void。

void left()
{}
void right()
{}

int myVar = 0;
myVar ? left() : right();

相当于

int myVar = 0;
myVar ? [](){}() : [](){}();

注意最后的额外 () - 我实际上称为 lambda。

您最初拥有的内容相当于

compiler_deduced_type var;
if (myVar)
    var = [](){};
else
    var = [](){};

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

void left()
{}
void right()
{}

int myVar = 0;
myVar ? left() : right();

is equivalent to

int myVar = 0;
myVar ? [](){}() : [](){}();

Note the extra () on the end- I actually called the lambda.

What you had originally is equivalent to

compiler_deduced_type var;
if (myVar)
    var = [](){};
else
    var = [](){};

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.

风为裳 2024-10-19 04:38:09

n3225 草案中条件运算符的规则曾提到过

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

到目前为止,所有其他替代方案(例如,将一个操作数转换为另一个操作数)都失败了,因此我们现在将按照该段落所述进行操作。我们将应用的转换由重载决策通过转换 a ? b : c 转换为 operator?(a, b, c) (对所谓函数的虚构函数调用)。如果你看看假想的运算符?的候选者是什么,你会发现(除其他外)

对于每个类型 T ,其中 T 是指针、成员指针或作用域枚举类型,都存在以下形式的候选运算符函数

T 运算符?(bool, T , T );

这包括 T 为类型 的候选者>void(*)()。这很重要,因为 lambda 表达式生成可以转换为此类类型的类对象。规范说

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

lambda 表达式无法转换为列出的任何其他参数类型,这意味着重载解析成功,找到单个 operator? 并将两个 lambda 表达式转换为函数指针。条件运算符部分的其余部分将照常进行,现在具有相同类型的条件运算符的两个分支。

这就是为什么你的第一个版本也可以,也是为什么 GCC 正确地接受它。然而,我真的不明白为什么你要显示第二个版本 - 正如其他人所解释的,它正在做一些不同的事情,并且它可以工作而另一个版本不能(在你的编译器上)也就不足为奇了。下次,最好不要在问题中包含无用的代码。

Rules for conditional operator in the draft n3225 says at one point

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.

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 into operator?(a, b, c) (an imaginary function call to a so-named function). If you look what the candidates for the imaginary operator? are, you find (among others)

For every type T , where T is a pointer, pointer-to-member, or scoped enumeration type, there exist candidate operator functions of the form

T operator?(bool, T , T );

And this includes a candidate for which T is the type void(*)(). This is important, because lambda expressions yield an object of a class that can be converted to such a type. The spec says

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.

The 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.

我是男神闪亮亮 2024-10-19 04:38:09

因为每个 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.

濫情▎り 2024-10-19 04:38:09

这两个代码片段都可以在 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?

-残月青衣踏尘吟 2024-10-19 04:38:09

它不会编译失败。它工作得很好。 您的编译器中可能没有启用 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 is void, and you can't do much with void.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文