Reputation: 29
As C++11 introduces the new uniform initialization syntax, many recommend to use it instead the old style syntax. At least, if it weren't for this so-called corner case:
struct Foo {
Foo(int){
std::cout << "default" << std::endl;
}
Foo(std::initializer_list<int>){
std::cout << "initlist" << std::endl;
}
};
int main(){
Foo f{200}; //prints "initlist"
}
Using a {}-always-style screams for trouble, especially in templates. There seem to be only three safe usages for the new syntax:
But there's also a case in which we have to use uniform initialization syntax: non-static data member initializers. For some reason, C++ can recognize
void Bar() {
Foo f(200);
}
but can't deal with
struct Bar {
Foo f(200);
};
Question #1: Why does the ()-syntax work inside a function but not a class? Does anyone know the rationale behind this?
Putting it all together, lastly we arrive at this silly case:
struct FooBar {
std::vector<int> bar(50); //doesn't work
std::vector<int> bar{50}; //not the intended effect
std::vector<int> bar = std::vector<int>(50); //works
};
Of course, you also can't use auto for data members. So I either have to awkwardly mix all syntaxes or not use these features at all.
Question #2: Did I misunderstand something? This can't be intended behavior, can it?
Upvotes: 2
Views: 257
Reputation: 141544
It's not allowed because it would lead to more instances of the "most vexing parse" which is already annoying. This isn't a major handicap because you can still use initialization syntax in the constructor body, or use the copy-initialization form.
If you bear in mind that the semantics of a brace-enclosed list is and has always been to provide a list of values to store in the object, then it's clear that std::vector<int> bar{50}
should (and does) create a vector containing one int.
Upvotes: 0
Reputation: 227370
Question #1: Why does the ()-syntax work inside a function but not a class? Does anyone know the rationale behind this?
Because it can look like a function declaration, and there already is enough confusion regarding that:
Foo f(); // function declaration. This still catches people out
But you can use ()
, just using the copy-initialization syntax:
T t = T(args);
Question #2: Did I misunderstand something? This can't be intended behavior, can it?
It is the design behaviour. It is unfortunate that it doesn't play very well with standard library containers of certain types (like std::vector<int>
in your example). You just have to remember that an implicit initializer_list
constructor trumps all other compatible constructors. You should strive to design your own classes such that they don't suffer from this problem.
See this related question: When to use a brace-enclosed initializer?
Upvotes: 1