2

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?

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
qrlpxy
  • 29
  • 2
  • I wasn't even away the syntax for the `struct Bar { Foo f(200); };` even *valid*. Man I need to read more and game less. oh wait.. nm. – WhozCraig Feb 17 '14 at 11:38
  • 2
    The main problem is that some people insist on calling it "uniform initialisation" (rather than "brace initialisation"), making it sound like something it isn't. I haven't noticed many (or indeed any) recommending that it be used everywhere; in any case, you should ignore any recommendation that doesn't explain its reasoning. – Mike Seymour Feb 17 '14 at 12:00
  • What is `std::vector(50)` supposed to do? Isn't this just the vector constructor overload that specifies the vector's size, instead of constructing a single `Foo` from that `50`? –  Feb 17 '14 at 12:02
  • To add to my previous comment, that's exactly what it means, and it is then rejected by the compiler because `Foo` does not have a default constructor. The `//works` line causes a compile-time error. I wouldn't call that "works". :) –  Feb 17 '14 at 12:07
  • 2
    @MikeSeymour I'll second that. There is no "uniform initialization" syntax in C++; you need to choose according to context, and what you want. I'd limit the new syntax to when an initialization list is wanted (and, of course, to cases where you're sure all compilers you might use will support it---something which is less than certain). – James Kanze Feb 17 '14 at 12:09
  • 1
    And of course, you don't _want_ to use `auto` for all variables. Most of the time, it's bad practice to hide the type; the exceptions are idiomatic uses like iterators. – James Kanze Feb 17 '14 at 12:11
  • @hvd The `//works` line should work on a compiler that properly supports in-class member initialisation, like [this one](http://ideone.com/0Dj8hD). – Mike Seymour Feb 17 '14 at 12:12
  • @MikeSeymour Now add the constructors to `Foo` that are part of this question, [like so](http://ideone.com/yOXpxx), and you'll get the error I was referring to. –  Feb 17 '14 at 12:13
  • @hvd: Fair enough; if you assume it's referring to the same type, then it does indeed fail to compile. Perhaps `vector` would be a better example, to avoid falling foul of such pedantic nitpicking. – Mike Seymour Feb 17 '14 at 12:22
  • @MikeSeymour I asked for clarification not to nit-pick, but because I think that maybe, the OP didn't mean to use that constructor in the first place. –  Feb 17 '14 at 12:26
  • @hvd You're right, I wrote the examples off the top of my head without testing them, thanks at MikeSeymour for fixing it! – qrlpxy Feb 17 '14 at 13:00
  • So if this is solved can you write up the solution and close this out? Those of us who strive to share knowledge really hate wasting time reading solved problems. Tx. – Dan Feb 21 '14 at 04:35

2 Answers2

1

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?

Community
  • 1
  • 1
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
0

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.

M.M
  • 138,810
  • 21
  • 208
  • 365