qrlpxy
qrlpxy

Reputation: 29

Coping with C++11 initialization syntaxes

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:

  1. explicitly requesting std::initializer_list-constructors
  2. POD-constructors
  3. default-constructors

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

Answers (2)

M.M
M.M

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

juanchopanza
juanchopanza

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

Related Questions