Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor taking std::initializer_list is preferred over other constructors

I compile this code below with GCC 11.1.0 with a flag -std=c++17. It occurs that on the stdout is printed initializer_list.

I compiled the same code with MSVC with the flag -std=c++17 but it printed "copy constructor". Which compiler is more compliant with the cpp standard? Is compiler free to choose one of the constructors?

#include <iostream>
using namespace std;

struct S
{
    S(int) { }
    S(initializer_list<int>) { cout << "initializer_list"; }
    S(const S&) { cout << "copy constructor"; }

    operator int() const { return 1; };
};

int main()
{
    S s1(20);
    S s2{ s1 };
}
like image 583
Michal Avatar asked Jul 10 '21 14:07

Michal


1 Answers

The compiler is pretty much never "free to choose" for stuff like this. If it were, we wouldn't be able to write pretty much any portable C++ code.

[over.match.list] does give priority to initializer_list constructors. Constructor function overloading under the rules of list initialization gets invoked at step 3.6. Steps 3.1-3.5 do not apply, as your type doesn't qualify for any of those cases. Step 3.1 is particularly interesting, as it is specifically meant to invoke copy constructors instead of doing other things, but it also only applies to aggregates. Which your type is not.

Since your type is implicitly convertible to int, and your type takes an initializer_list<int>, there is a valid way to build an initializer_list that matches a constructor for the type in question. Therefore, this is the constructor [over.match.list] will select.

So in this case, VC++ is wrong. As is Clang, apparently.

like image 147
Nicol Bolas Avatar answered Oct 24 '22 12:10

Nicol Bolas