6

Consider the following program:

struct X
{
    X(int, int) { }
    X(X&&) { }
};

int main()
{
    X x( {0, 1} ); // Doesn't compile on ICC 13.0.1, compiles on
                   // Clang 3.2, GCC 4.7.2, and GCC 4.8.0 beta.
}

When compiled with GCC 4.7.2, GCC 4.8.0, and Clang 3.2, this program does the following (*):

  1. Constructs a temporary of type X passing values 0 and 1 to the constructor, then;
  2. Move-constructs X from that temporary.

With ICC 13.0.1, instead, it doesn't compile.

QUESTION #1: Who is right?

(*) Actually, the creation of the temporary and the call to the move constructor are elided, but compiling with the -fno-elide-constructors option and adding some printouts to the constructors reveals that this is what is going on.


Now consider the following, slight variation of the above program, where uniform initialization is used to direct-initialize x:

int main()
{
    X x{ {0, 1} }; // ERROR! Doesn't compile.
//     ^........^
}

I would not expect the use of braces instead of parentheses to change anything here, but it somehow does: this program doesn't compile on any of the compilers I've tested it on (Clang 3.2, GCC 4.7.2, GCC 4.8.0 beta, and ICC 13.0.1).

QUESTION #2: Why?

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • I'm curious as to what errors you get with the second one. – Nicol Bolas Feb 28 '13 at 02:42
  • @NicolBolas: Check it out [here](http://liveworkspace.org/code/10CpDi$73), you can choose the compiler. – Andy Prowl Feb 28 '13 at 02:44
  • You've been here long enough to know that this isn't how SO works. If it's pertinent, you put it in the question, not link to some live-workspace page. – Nicol Bolas Feb 28 '13 at 02:47
  • 1
    @NicolBolas: I just did not think it is pertinent. It seems to me that this example is simple enough to allow anybody to reason on it without depending on a compiler, but if someone is curious about what concrete message did any of those 4 compilers emit, he can try it. Copy-pasting a 10 lines program is quick enough, but since you asked, I made that even easier and provided a link. – Andy Prowl Feb 28 '13 at 02:54
  • What's wrong with good old plain `X x{0,1};`? – David Rodríguez - dribeas Feb 28 '13 at 04:52

1 Answers1

2

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.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • @AndyProwl Yes. I don't have ICC and I think the first example is correct. (Indeed, I think the second one is too.) – Potatoswatter Feb 28 '13 at 03:04
  • So you think both programs should compile? (btw, [here](http://liveworkspace.org/code/10CpDi$87) you can select ICC) – Andy Prowl Feb 28 '13 at 03:08
  • @AndyProwl Yep. The trick I mentioned also satisfies Clang but not ICC, which expects an expression. It *certainly* has no reason to expect an expression there. Braced-init-lists may contain expressions, or other braced-init-lists. – Potatoswatter Feb 28 '13 at 03:13
  • I was reading the same paragraph you quoted, and I also got to the item in bold, and there I stopped. Are you implying GCC didn't, and skipped right to the last item? – Andy Prowl Feb 28 '13 at 03:18
  • @Andy Yep. And Clang correctly executed the next item (bind a reference), but still skipped #3 on the second pass. – Potatoswatter Feb 28 '13 at 03:23
  • Ok, +1 for now. Tomorrow I'll read through it again with a fresher mind ;-) – Andy Prowl Feb 28 '13 at 03:24
  • OK, indeed this MUST be a bug. There is no reason why [this one](http://liveworkspace.org/code/1AzAfL$2) should work and [this one](http://liveworkspace.org/code/31UWw9$0) should not. – Andy Prowl Feb 28 '13 at 13:49