Home > Mobile >  Copy-elision in direct initialization from braced-init-list
Copy-elision in direct initialization from braced-init-list

Time:08-13

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?

CodePudding user response:

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.

CodePudding user response:

My understanding that both are right.

First, any compiler which opts to not do copy elision is right, because copy-elision is not mandatory, so Clang is not wrong.

Second, It's allowed in certain circumstances which are met here. And thus GCC is also not wrong.

Also consider the emphasised sentence of [class.copy.elision]/1 (emphasis mine):

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects.

You can read the "side-effect" part as "even if the result of selected constructor is different from what copy constructor does". It means that such a difference is not considered a problem by the standard.

  • Related