适用于不同lambdas的三元操作员会产生不一致的结果

发布于 2025-01-20 20:52:38 字数 1087 浏览 1 评论 0原文

考虑以下使用三元运算符获取两个 lambda 的公共函数指针类型

int main() {
  true ? [](auto) noexcept {} : [](int) {};
}

GCC-trunk 仅在 C++14 中接受它,但在 C++17/20 中拒绝它 (Demo):

<source>:2:8: error: operands to '?:' have different types 'main()::<lambda(auto:1)>' and 'main()::<lambda(int)>'
    2 |   true ? [](auto) noexcept {} : [](int) {};
      |   ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Clang-trunk 在所有 C++14/17/20 模式下接受它 (演示)。

MSVC-trunk 仅在 C++20 中接受它,但在 C++14/17 中拒绝它(Demo):

<source>(2): error C2446: ':': no conversion from 'main::<lambda_01e5bb79b5a210014fb78333f6af80f9>' to 'main::<lambda_57cf6f5767bc1bee4c1e1d9859a585d2>'
<source>(2): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

哪个编译器是正确的?

Consider the following which uses the ternary operator to get the common function pointer type of the two lambdas

int main() {
  true ? [](auto) noexcept {} : [](int) {};
}

GCC-trunk only accepts it in C++14 but rejects it in C++17/20 with (Demo):

<source>:2:8: error: operands to '?:' have different types 'main()::<lambda(auto:1)>' and 'main()::<lambda(int)>'
    2 |   true ? [](auto) noexcept {} : [](int) {};
      |   ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Clang-trunk accepts it in all C++14/17/20 mode (Demo).

MSVC-trunk only accepts it in C++20 but rejects it in C++14/17 with (Demo):

<source>(2): error C2446: ':': no conversion from 'main::<lambda_01e5bb79b5a210014fb78333f6af80f9>' to 'main::<lambda_57cf6f5767bc1bee4c1e1d9859a585d2>'
<source>(2): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

Which compiler is right?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(1

夢归不見 2025-01-27 20:52:38

由于每个lambda表达式都有唯一的类型,并且lambda表达式都不可转换到另一个类型, [expr.cond]/6 适用。

如果第二和第三操作数没有相同的类型,并且要么(可能是CV合格的)类类型,则使用过载分辨率来确定要应用于操作数的转换(如果有)([[ match.oper],[over.built])。如果过载分辨率失败,则该程序的形式不佳。
否则,将应用这样确定的转换,并在本第一项的其余部分使用转换后的操作数代替原始操作数。

候选人是( [over.built]/25 ):

对于每种类型t,其中t是指针,指针到成员或范围的枚举类型,存在表单的候选操作员功能

  t运算符?:( bool,t,t);
 

我相信过载分辨率应该应该解决。在这种情况下,成功,因此,两个操作数都应转换为void(*)(int)

也就是说,IMO Clang是正确的。


编辑:我相信[](auto)noexcept {}可转换为void(*)(int),因为它具有转换功能模板code> template&lt; T&GT级操作员fp(),其中fpvoid(*)(t)(t) [expr.prim.lambda.closure]/9 )。

对于没有lambda捕获的通用lambda,闭合类型具有转换功能模板到指针的功能。
转换功能模板具有相同的发明模板参数列表,并且函数指针具有与函数调用操作员模板相同的参数类型。
函数指针的返回类型应表现得像是声明类型的指定符,表示相应函数呼叫呼叫操作员模板的返回类型。

而且我相信模板参数扣除应该使此转换模板可作为转换为void(*)(int) [temp.deduct.conv]/5 )。

模板参数扣除是通过比较转换函数模板的返回类型(调用p)来完成的。如[temp.deduct.type]。

[...]

通常,扣除过程试图找到模板参数值,该值将使推论aa相同。
但是,可以忽略a的某些属性:

  • [...]
  • 如果原始a是功能指针或指针到会员功能类型,则其noexcept

Since each lambda expression has a unique type, and neither lambda expression is convertible to the other, [expr.cond]/6 applies.

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 ([over.match.oper], [over.built]). 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 subclause.

The candidates are ([over.built]/25):

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

I believe that overload resolution should succeed in this case, and as a result, both operands should be converted to void(*)(int).

That is, IMO clang is correct.


Edit: I believe that [](auto) noexcept {} is convertible to void(*)(int), because it has a conversion function template template<class T> operator FP(), where FP is void(*)(T) ([expr.prim.lambda.closure]/9).

For a generic lambda with no lambda-capture, the closure type has a conversion function template to pointer to function.
The conversion function template has the same invented template parameter list, and the pointer to function has the same parameter types, as the function call operator template.
The return type of the pointer to function shall behave as if it were a decltype-specifier denoting the return type of the corresponding function call operator template specialization.

And I believe that template argument deduction should make this conversion template usable as a conversion to void(*)(int) ([temp.deduct.conv]/5).

Template argument deduction is done by comparing the return type of the conversion function template (call it P) with the type specified by the conversion-type-id of the conversion-function-id being looked up (call it A) as described in [temp.deduct.type].

[...]

In general, the deduction process attempts to find template argument values that will make the deduced A identical to A.
However, certain attributes of A may be ignored:

  • [...]
  • If the original A is a function pointer or pointer-to-member-function type, its noexcept.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文