Search code examples
c++language-lawyercopy-elisiondirect-initialization

Copy-elision in direct initialization from braced-init-list


In the following program the object A a is directly initialized from braced-init-list {A{}}:

#include <iostream>

struct A {
    int v = 0;
    A() {}
    A(const A &) : v(1) {}
};

int main() {
    A a({A{}});
    std::cout << a.v;
}

MSVC and GCC print 0 here meaning that copy-elision takes place. And Clang prints 1 executing the copy-constructor. Online demo: https://gcc.godbolt.org/z/1vqvf148z

Which compiler is right here?


Solution

  • Which compiler is right here?

    I think that clang is right in using the copy constructor and printing 1 for the reason(s) explained below.

    First note that A a({A{}}); is direct-initialization as can be seen from dcl.init#16.1:

    1. The initialization that occurs:

    16.1) for an initializer that is a parenthesized expression-list or a braced-init-list,

    16.2) for a new-initializer,

    16.3) in a static_­cast expression ([expr.static.cast]),

    Now, dcl.init#17.6 is applicable here:

    17.6) Otherwise, if the destination type is a (possibly cv-qualified) class type:

    17.6.1) If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object. [ Example: T x = T(T(T())); calls the T default constructor to initialize x. — end example ]

    17.6.2) Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated ([over.match.ctor]), and the best one is chosen through overload resolution ([over.match]). Then:

    17.6.2.1) If overload resolution is successful, the selected constructor is called to initialize the object, with the initializer expression or expression-list as its argument(s).

    (emphasis mine)

    This means that the copy constructor(which is the selected constructor here) will be used/called to initialize the object named a with the expression-list as its argument and since in your copy ctor's member initializer list you're initializing a.v to 1, the output printing 1 of clang is correct.