Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will using brace-init syntax change construction behavior when an initializer_list constructor is added later?

Tags:

c++

c++11

Suppose I have a class like this:

class Foo
{
public:
    Foo(int something) {}
};

And I create it using this syntax:

Foo f{10};

Then later I add a new constructor:

class Foo
{
public:
    Foo(int something) {}
    Foo(std::initializer_list<int>) {}
};

What happens to the construction of f? My understanding is that it will no longer call the first constructor but instead now call the init list constructor. If so, this seems bad. Why are so many people recommending using the {} syntax over () for object construction when adding an initializer_list constructor later may break things silently?

I can imagine a case where I'm constructing an rvalue using {} syntax (to avoid most vexing parse) but then later someone adds an std::initializer_list constructor to that object. Now the code breaks and I can no longer construct it using an rvalue because I'd have to switch back to () syntax and that would cause most vexing parse. How would one handle this situation?

like image 497
void.pointer Avatar asked Sep 18 '14 17:09

void.pointer


1 Answers

What happens to the construction of f? My understanding is that it will no longer call the first constructor but instead now call the init list constructor. If so, this seems bad. Why are so many people recommending using the {} syntax over () for object construction when adding an initializer_list constructor later may break things silently?

On one hand, it's unusual to have the initializer-list constructor and the other one both be viable. On the other hand, "universal initialization" got a bit too much hype around the C++11 standard release, and it shouldn't be used without question.

Braces work best for like aggregates and containers, so I prefer to use them when surrounding some things which will be owned/contained. On the other hand, parentheses are good for arguments which merely describe how something new will be generated.

I can imagine a case where I'm constructing an rvalue using {} syntax (to avoid most vexing parse) but then later someone adds an std::initializer_list constructor to that object. Now the code breaks and I can no longer construct it using an rvalue because I'd have to switch back to () syntax and that would cause most vexing parse. How would one handle this situation?

The MVP only happens with ambiguity between a declarator and an expression, and that only happens as long as all the constructors you're trying to call are default constructors. An empty list {} always calls the default constructor, not an initializer-list constructor with an empty list. (This means that it can be used at no risk. "Universal" value-initialization is a real thing.)

If there's any subexpression inside the braces/parens, the MVP problem is already solved.

like image 99
Potatoswatter Avatar answered Nov 11 '22 00:11

Potatoswatter