Search code examples
c++c++11list-initialization

what is the return type of list initialisation?


Does anyone would like to explain why case 1 and case 2 have different output for this code snippet.

struct A {
    A() { cout << "A()" << endl; }
    A(int i) { cout << "A(int)" << endl; }
    A(const A&) { cout << "A(const A&)" << endl; }
    A(A&&) noexcept { cout << "A(A&&)" << endl; }
    A& operator=(const A&) { cout << "operator=(const A&)" << endl; return *this; }
    A& operator=(A&&) noexcept { cout << "operator=(A&&)" << endl; return *this; }
    friend bool operator< (const A&, const A&) { return true; }
};

int main() {
    std::set<A> aSet;
    aSet.insert(1);       // case 1
    //aSet.insert({1});   // case 2

    return 0;
}

For case 1, the output is:

A(int)
A(A&&)

and for case 2 is:

A(int)
A(const A&)

The compiler version is:

g++ --version g++-7 (SUSE Linux) 7.2.1 20170901 [gcc-7-branch revision 251580] Copyright (C) 2017 Free Software Foundation, Inc.


Solution

  • The relevant overloads of std::set::insert are:

    std::pair<iterator,bool> insert( value_type const& value ); // #1
    std::pair<iterator,bool> insert( value_type&& value );      // #2
    void insert( std::initializer_list<value_type> ilist );     // #6
    

    When you call aSet.insert(1); that invokes #2 in preference to #1 - which is going to create a new A via A(int ) and then move it into the set. Hence, A(A&& ).

    However, when you call aSet.insert({1}), the overload selected is #6. Whenever you use list-initialization, the std::initializer_list candidate is strongly preferred (basically, we do overload resolution first only considering those candidates and then, only if we don't find one, do we redo overload resolution considering the rest). Since std::initializer_list is backed by a const array, once we create our A via A(int ) we must copy it out - we cannot move it. Hence A(A const& ).