It's simply a bug in all the compilers. §8.5.4/3 says,
List-initialization of an object or reference of type T is defined as follows:
— If the initializer list has no elements and T is a class type with a default constructor, the object is
value-initialized.
— Otherwise, if T is an aggregate, aggregate initialization is performed (8.5.1).
— Otherwise, if T is a specialization of std::initializer_list, an initializer_list object is constructed as described below and used to initialize the object …
— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
— Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary.
— Otherwise, if the initializer list has a single element, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.
…
There are quite a few cases. Note that you're actually list-initializing an prvalue temporary object bound to the rvalue reference.
As for GCC, I think it's trying to apply the last item mentioned above, for a single-element initializer list. If I change the constructor signature to X(X&&, int = 3)
, then the initializer { {0, 1} }
fails but { {0, 1}, 3 }
succeeds. The single item should succeed because because the element is a braced-init-list, and I believe that case is supposed to allow extra enclosing braces, analogous to parens. But the failure is similar to other shortcomings of GCC with brace elision.
My high-level impression is that the problems arise when the compiler tries to treat the list as an object with a type, which it's not. It's hard to convert that back to something like a parenthesized argument list.
Looking more specifically at the error messages (thanks for the LWS link),
ICC insists it expects an expression. This is wrong because according to the basic grammar, braced-init-lists can enclose other braced-init-lists, not only expressions.
Clang says "candidate constructor not viable: cannot convert initializer list argument to 'X'" but it works if the conversion is explicit, using X x{ X{0, 0 } };
. That doesn't make sense. It's not a conversion, because a list doesn't have a type to convert from. It's list-initialization.
GCC says "no known conversion for argument 1 from '' to 'X&&'" suggesting it didn't even get to the point of defining a temporary to bind to the reference. As with Clang it seems to be attempting spurious conversion, and specifying X{0,0}
fixes it.