c++g++clang++

Why does GCC forward the arguments of this vector constructor to the constructor of the contained type while Clang doesn't?


Consider the following dummy type:

struct Foo{
Foo(int x) : x{x}{}

template<std::ranges::range R>
Foo(R r) : x{std::accumulate(r.begin(), r.end(), 0, [](int x, const auto& foo){return x + foo.x;})}{}

int x;
};

If I now brace initialize a std::vector<Foo> with another std::vector<Foo>, the result differs by compiler.

std::vector<Foo> foos{Foo{1}, Foo{2}, Foo{3}};
std::vector<Foo> foos_copy{foos};

While in clang foos == foos_copy, GCC forwards the vector to the range-based constructor of Foo, resulting in foos_copy containing a single element only, as if it had been

std::vector<Foo> foos_copy{{foos}};

See here for full example: https://godbolt.org/z/G1dhbKsoc

I'm just wondering why this difference exists and which compiler is correct.


Solution

  • CWG2137 changed the rules for list-initialization of a class type from its own type. Previously, the copy/move constructor took precedence. Now, an std::initializer_list constructor takes precedence—but if no viable one exists, then it still falls back to the copy/move constructor (or some other constructor that can accept the same type).

    This issue is curious because the resolution was adopted in 2016, but Richard Smith, the submitter of the issue and major contributor to Clang, didn't actually implement the resolution in Clang. I asked Richard whether it's because he tried implementing it and found that it broke too much code, but he didn't reply. I see that there has been a recent effort to implement it in Clang, but it was reverted 5 days after being merged, which may not be enough time for external users to have reported regressions.

    Anyway, the standard says that foos_copy should end up with only 1 element, and depending on how you look at it, either Clang hasn't implemented it yet, or the resolution itself is wrong and will need to be reverted eventually. (Note that, in contrast to what user17732522 said in the comments, CWG2742 is not arguing to revert CWG2137. Instead, the author of the proposed resolution to CWG2742 accidentally wrote wording that would revert CWG2137.)