Search code examples
c++c++17copy-elisionaggregate-initialization

Can copy elision be perfomed in aggregate initialization in c++17?


Given:

//C++17
#include <string>
struct Foo {
    int i;
    std::string str;
};

int main() {
    Foo foo{1, std::string("Hello, world!")};
}

Can Foo::i and Foo::str be directly initialized from 1 and std::string(...) instead of being copied into them, and explain why can/can't using C++17 standard(probably some code for testing purpose)?

If they can't, how many copies are required?


Solution

  • Aggregate initialization basically performs element-wise copy-initialization. So this:

    struct Foo {
        int i;
        std::string str;
    };
    
    Foo foo{1, std::string("Hello, world!")};
    

    does the same initializations as:

    int i = 1;
    std::string str = std::string("Hello, world!");
    

    And we have a new rule in C++17 that says that:

    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 ]

    which means that the second initialization must behave as if you'd written:

    std::string str("Hello, world!");
    

    That is, zero copies.


    A nice demonstration of the new rule is the following example:

    struct X {
        X(int ) { }
        X(X&& ) = delete;
    };
    
    struct Y {
        X x;
    };
    
    int main() {
        Y y{X{4}}; // ill-formed in C++14 due to deleted move ctor
                   // ok in C++17, no move required
    }