为什么这些 lambda 捕获的值具有不同的类型?

发布于 2025-01-14 05:53:22 字数 3130 浏览 2 评论 0原文

两个 int 变量(其中一个是引用)在被 lambda 捕获时,在 MSVC 的 c++20 中类型不同。为什么?

Scott 在编译时确定类型的技术可以为 c++14 和 17 生成预期结果,但不能为 c++20 生成预期结果。然而,这种奇怪的差异似乎也出现在早期版本的其他编译中。

具体来说,两个 int,一个是引用,当被值或引用捕获时会产生不同的类型。 intint &。然而早期版本会产生预期的类型。当通过值捕获时,两者的类型均为 int。当通过引用捕获时,两者都是int &

// Scott Meyer's compile time Type Deduction technique
template<typename T>
class TD;

int main()
{
    int i{ 1 };
    int& ri = i;

    [i, ri]() mutable {
        TD<decltype(i)> WhatAmI1;    // c++14, c++17 TD<int>, c++20 TD<int>
        TD<decltype(ri)> WhatAmI2;   // c++14, c++17 TD<int>, c++20 TD<int &>
    }();

    [&i, &ri]() mutable {
        TD<decltype(i)> WhatAmI3;    // c++14, c++17 TD<int&>, c++20 TD<int>
        TD<decltype(ri)> WhatAmI4;   // c++14, c++17 TD<int&>, c++20 TD<int &>
    }();
}

进一步探索这一点,生成的代码符合预期,并且捕获的值和捕获的引用都是相同的。

int main()
{
    int i{ 1 };
    int& ri = i;

    [i, ri]() mutable {
        i += 2;
        ri += 3;
    }();

    [&i, &ri]() {
        i += 2;
        ri += 3;
    }();
}

     [i, ri]() mutable {
         i += 2;
00007FF7F506187F 48 8B 85 E0 00 00 00 mov         rax,qword ptr [this]
00007FF7F5061886 8B 00                mov         eax,dword ptr [rax]
00007FF7F5061888 83 C0 02             add         eax,2
00007FF7F506188B 48 8B 8D E0 00 00 00 mov         rcx,qword ptr [this]
00007FF7F5061892 89 01                mov         dword ptr [rcx],eax
         ri += 3;
00007FF7F5061894 48 8B 85 E0 00 00 00 mov         rax,qword ptr [this]
00007FF7F506189B 8B 40 04             mov         eax,dword ptr [rax+4]
00007FF7F506189E 83 C0 03             add         eax,3
00007FF7F50618A1 48 8B 8D E0 00 00 00 mov         rcx,qword ptr [this]
00007FF7F50618A8 89 41 04             mov         dword ptr [rcx+4],eax



     [&i, &ri]() {
         i += 2;
00007FF7F506190F 48 8B 85 E0 00 00 00 mov         rax,qword ptr [this]
00007FF7F5061916 48 8B 00             mov         rax,qword ptr [rax]
00007FF7F5061919 8B 00                mov         eax,dword ptr [rax]
00007FF7F506191B 83 C0 02             add         eax,2
00007FF7F506191E 48 8B 8D E0 00 00 00 mov         rcx,qword ptr [this]
00007FF7F5061925 48 8B 09             mov         rcx,qword ptr [rcx]
00007FF7F5061928 89 01                mov         dword ptr [rcx],eax
         ri += 3;
00007FF7F506192A 48 8B 85 E0 00 00 00 mov         rax,qword ptr [this]
00007FF7F5061931 48 8B 40 08          mov         rax,qword ptr [rax+8]
00007FF7F5061935 8B 00                mov         eax,dword ptr [rax]
00007FF7F5061937 83 C0 03             add         eax,3
00007FF7F506193A 48 8B 8D E0 00 00 00 mov         rcx,qword ptr [this]
00007FF7F5061941 48 8B 49 08          mov         rcx,qword ptr [rcx+8]
00007FF7F5061945 89 01                mov         dword ptr [rcx],eax
     }();

链接到编译器资源管理器https://godbolt.org/z/oe9KPcWWv

Two int variables, one a reference, when captured by lambdas, differ in type in MSVC's c++20. Why?

Scott's technique to determine type at compile time produces expected results for c++14 and 17 but not for c++20. However, it seems this odd difference occurs in other compiles for earlier versions too.

Specifically, two ints, one a reference, when captured by either value or reference produce differing types. int and int &. Yet earlier versions produces expected types. When captured by value both were typed int. When captured by reference both were int &.

// Scott Meyer's compile time Type Deduction technique
template<typename T>
class TD;

int main()
{
    int i{ 1 };
    int& ri = i;

    [i, ri]() mutable {
        TD<decltype(i)> WhatAmI1;    // c++14, c++17 TD<int>, c++20 TD<int>
        TD<decltype(ri)> WhatAmI2;   // c++14, c++17 TD<int>, c++20 TD<int &>
    }();

    [&i, &ri]() mutable {
        TD<decltype(i)> WhatAmI3;    // c++14, c++17 TD<int&>, c++20 TD<int>
        TD<decltype(ri)> WhatAmI4;   // c++14, c++17 TD<int&>, c++20 TD<int &>
    }();
}

Exploring this further, the code produced is as expected and is the same for both captured values and captured references.

int main()
{
    int i{ 1 };
    int& ri = i;

    [i, ri]() mutable {
        i += 2;
        ri += 3;
    }();

    [&i, &ri]() {
        i += 2;
        ri += 3;
    }();
}

     [i, ri]() mutable {
         i += 2;
00007FF7F506187F 48 8B 85 E0 00 00 00 mov         rax,qword ptr [this]
00007FF7F5061886 8B 00                mov         eax,dword ptr [rax]
00007FF7F5061888 83 C0 02             add         eax,2
00007FF7F506188B 48 8B 8D E0 00 00 00 mov         rcx,qword ptr [this]
00007FF7F5061892 89 01                mov         dword ptr [rcx],eax
         ri += 3;
00007FF7F5061894 48 8B 85 E0 00 00 00 mov         rax,qword ptr [this]
00007FF7F506189B 8B 40 04             mov         eax,dword ptr [rax+4]
00007FF7F506189E 83 C0 03             add         eax,3
00007FF7F50618A1 48 8B 8D E0 00 00 00 mov         rcx,qword ptr [this]
00007FF7F50618A8 89 41 04             mov         dword ptr [rcx+4],eax



     [&i, &ri]() {
         i += 2;
00007FF7F506190F 48 8B 85 E0 00 00 00 mov         rax,qword ptr [this]
00007FF7F5061916 48 8B 00             mov         rax,qword ptr [rax]
00007FF7F5061919 8B 00                mov         eax,dword ptr [rax]
00007FF7F506191B 83 C0 02             add         eax,2
00007FF7F506191E 48 8B 8D E0 00 00 00 mov         rcx,qword ptr [this]
00007FF7F5061925 48 8B 09             mov         rcx,qword ptr [rcx]
00007FF7F5061928 89 01                mov         dword ptr [rcx],eax
         ri += 3;
00007FF7F506192A 48 8B 85 E0 00 00 00 mov         rax,qword ptr [this]
00007FF7F5061931 48 8B 40 08          mov         rax,qword ptr [rax+8]
00007FF7F5061935 8B 00                mov         eax,dword ptr [rax]
00007FF7F5061937 83 C0 03             add         eax,3
00007FF7F506193A 48 8B 8D E0 00 00 00 mov         rcx,qword ptr [this]
00007FF7F5061941 48 8B 49 08          mov         rcx,qword ptr [rcx+8]
00007FF7F5061945 89 01                mov         dword ptr [rcx],eax
     }();

Link to compiler explorer https://godbolt.org/z/oe9KPcWWv

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

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

发布评论

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

评论(1

土豪 2025-01-21 05:53:22

C++20 模式下的 Clang、GCC 和 MSVC 的行为对于所有支持 lambda 的标准版本都是正确的。 decltype(i)decltype(ri) 生成命名变量的类型。它不会被重写以引用闭包对象的成员,就像 decltype((ri)) 的情况一样。 (参见例如 [expr.prim.lambda.capture] /14 在 C++17 草案 N4659 中对此进行了专门处理)

显然,MSVC 在 C++20 之前的标准模式的默认行为在处理 lambda 的方式上是不一致的。根据文档必须给出标志 /Zc:lambda 来处理符合标准的 lambda。使用此选项,MSVC 也会为 C++17 和 C++14 模式生成与 C++20 模式相同的结果。

例如,请参阅此错误报告 已作为非错误关闭,并附有使用此标志的说明。另请注意,它似乎未包含在 /permissive- 中。

The behavior of Clang, GCC and MSVC in C++20 mode is correct for all standard versions supporting lambdas. decltype(i) and decltype(ri) yield the type of the named variables. It is not rewritten to refer to the members of the closure object, as would be the case for decltype((ri)). (see e.g. [expr.prim.lambda.capture]/14 in C++17 draft N4659 handling this specifically)

Apparently MSVC's default behavior for standard modes before C++20 is non-conforming in how lambdas are handled. According to the documentation the flag /Zc:lambda must be given to handle lambdas standard-conforming. With this option MSVC produces the same result as in C++20 mode for C++17 and C++14 mode as well.

See for example this bug report which was closed as not-a-bug with instruction to use this flag. Also note that it doesn't seem to be included in /permissive-.

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