Using lambda in default initializer vs using member function

381 Views Asked by At

Is there any difference in using "one-time" lambda in default initializer and using plain old member function?

struct A
{
    int i;
    int j = [&]
    // something non-trivial,
    // that requires multiple
    // statements and depends
    // on upper data members
    {
        int f = 0;
        for (int k = 0; k < i; ++k) {
            f += k;
        }
        return f;
    }();
    A(int k) : i(k) { ; }
};

Versus:

struct A
{
    int i;
    int J() const
    {
        int f = 0;
        for (int k = 0; k < i; ++k) {
            f += k;
        }
        return f;
    }
    int j = J();
    A(int k) : i(k) { ; }
};

The only I see is the downside of the second approach: here extra symbol J introduced into namespace of class A. Are there another distinctions?

2

There are 2 best solutions below

0
vordhosbn On

Regarding performance, there is no difference in the gcc 7.1 -O3 compiled code. Both implementations yield the same assembly.

The test code:

int callerFunc(int init)
{
    A st(init);
    return st.j; 
}

gets compiled to:

callerFunc(int):
        test    edi, edi
        jle     .L7
        lea     eax, [rdi-1]
        cmp     eax, 7
        jbe     .L8
        pxor    xmm0, xmm0
        mov     edx, edi
        xor     eax, eax
        movdqa  xmm1, XMMWORD PTR .LC0[rip]
        shr     edx, 2
        movdqa  xmm2, XMMWORD PTR .LC1[rip]
.L5:
        add     eax, 1
        paddd   xmm0, xmm1
        paddd   xmm1, xmm2
        cmp     eax, edx
        jb      .L5
        movdqa  xmm1, xmm0
        mov     edx, edi
        and     edx, -4
        psrldq  xmm1, 8
        paddd   xmm0, xmm1
        movdqa  xmm1, xmm0
        cmp     edi, edx
        psrldq  xmm1, 4
        paddd   xmm0, xmm1
        movd    eax, xmm0
        je      .L10
.L3:
        lea     ecx, [rdx+1]
        add     eax, edx
        cmp     edi, ecx
        jle     .L1
        add     eax, ecx
        lea     ecx, [rdx+2]
        cmp     edi, ecx
        jle     .L1
        add     eax, ecx
        lea     ecx, [rdx+3]
        cmp     edi, ecx
        jle     .L1
        add     eax, ecx
        lea     ecx, [rdx+4]
        cmp     edi, ecx
        jle     .L1
        add     eax, ecx
        lea     ecx, [rdx+5]
        cmp     edi, ecx
        jle     .L1
        add     eax, ecx
        lea     ecx, [rdx+6]
        cmp     edi, ecx
        jle     .L1
        add     eax, ecx
        add     edx, 7
        lea     ecx, [rax+rdx]
        cmp     edi, edx
        cmovg   eax, ecx
        ret
.L7:
        xor     eax, eax
.L1:
        rep ret
.L10:
        rep ret
.L8:
        xor     eax, eax
        xor     edx, edx
        jmp     .L3
.LC0:
        .long   0
        .long   1
        .long   2
        .long   3
.LC1:
        .long   4
        .long   4
        .long   4
        .long   4
6
Nikos Kazazakis On

A couple of differences come to mind:

  1. The lambda cannot be overloaded. Hence, any inherited class will use the same function.
  2. The lambda allows you to capture local variables by default, which may cause bugs if things are renamed/re-ordered. If you pass them explicitly, this can be mitigated. While re-ordering is dangerous for methods and lambdas alike, default lambda capture is more volatile because the variable you re-order will be passed into the lambda implicitly.