3

Could somebody tell me the theory behind this?

Why the last call doesn't compile?

test.cc: In function ‘int main()’: test.cc:15:12: error: too many braces around initializer for ‘int’ [-fpermissive] test.cc:15:12:

error: invalid conversion from ‘’ to ‘int’ [-fpermissive] test.cc:9:6: error: initializing argument 1 of ‘void f(std::initializer_list)’ [-fpermissive] test.cc:15:12:

error: aggregate value used where an integer was expected

I think either c++11 or g++ 4.7 is broken on this. Thank you!

#include <initializer_list> class A { public: A(const std::initializer_list<int>) {} }; void f(const std::initializer_list<int>) {} int main() { A({1}); // Compile OK f({1}); // Compile OK A({{{1}}}); // Compile OK //f({{{1}}}); // Compile Error. } 
2
  • Thanks. I'm talking about the stardard header initializer_list in c++11. Commented Apr 7, 2013 at 6:08
  • Interesting. Clang just throws a warning about extra braces in both cases. Commented Apr 7, 2013 at 6:38

2 Answers 2

2

Here is what I believe GCC is thinking.

This is your program with 1 extra line, and the interesting lines numbered.

int main() { A({1}); // 1. Compile OK f({1}); // 2. Compile OK A{{1}}; // 3. Compile OK, equivalent to 1. A({{{1}}}); // 4. Compile OK //f({{{1}}}); // 5. Compile Error. } 

Why does GCC compile 4 but not 5?

For clarity, suppose the construction at #4 actually declared something:

A a{{{1}}}; // 4a. Compile OK 

GCC asks if the argument of the constructor, which is {{1}} is implicitly convertible to A. So is:

A{{1}} 

a valid conversion from {{1}} to A? Yes it is - as per 3.

This reasoning, of course, is not applicable to #5; hence error.

If you want to stop GCC from accepting #4, then block the enabling conversion by making the enabling constructor explicit:

class A { public: explicit A(const std::initializer_list<int> il) {} }; 

Then #4 will give the error:

error: converting to ‘A’ from initializer list would use explicit constructor ‘A::A(std::initializer_list<int>)’ 
Sign up to request clarification or add additional context in comments.

3 Comments

I noticed that f({1}) and f({{1}}) both compile without error. what is the difference between them?
My clumsy account of the constructor conversion from {{1}} hits the board somewhere but Johannes gets the cigar. I think his words "the second goes to the initializer list recursively." probably also explain why f({{1}}) is OK. But I'll defer to him on that.
The first brace is for the init list. The second brace is for the int.
2

A {1} can initialize an int. A {{1}} probably should not - there is a defect report on tbe committee for that. GCC forbids that and clang just tends to emit warnings about redundant braces currently.

When X is a class that has copy or move constructors, then X ({...}) may invoke invoke one of them. Notice that X {...} may too, but is restricted to not allow user defined conversions (for a copy or move constructor).

Now with your A ({{{1}}}) the first brace is consumed by the copy/move constructor. The second goes to the initializer list recursively. And the third goes to the contained int.

According to the Standard, adding one more brace will break for A ({{{{1}}}}). Because the second brace would need to be consumed by a copy/move constructor of A but need a user defined conversion sequence. The same holds for A {{{{1}}}}, which is invalid for this reason too.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.