Home > Blockchain >  Why do these lambda captured values have different types?
Why do these lambda captured values have different types?

Time:03-14

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

CodePudding user response:

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

  • Related