11

C++11 allows in-class initialization:

struct Foo{
    std::vector<std::string> v{3}; // vector of 3 empty strings
};

If we wanted to initialize in-class a vector of ints, we would get something else:

struct Foo{
    std::vector<int> v{3}; // vector of one element with value 3
};

This issue seems to be a limitation of the language, as discussed in previous questions. However, if this were not an in-class initialization, we would be able to use parentheses instead of braces, and get the desired result:

std::vector<int> v(3); // vector of three zeros

However, we cannot do this in a class because of most vexing parse:

struct Foo{
    std::vector<int> v(3); // most vexing parse; doesn't compile
};

Of course, it's debatable whether the code above is good design practice, since we can easily just move what we're trying to do into a constructor. But temporarily putting that aside, is there a way to perform the desired initialization, as closely as possible to the first std::string example, which works with no problem?

JFMR
  • 23,265
  • 4
  • 52
  • 76
xdavidliu
  • 2,411
  • 14
  • 33
  • 3
    How is `std::vector v(3);` an example of *most vexing parse*? I'm unable to understand that. And btw, the code involving *most vexing parse* **does** compile; just that it is meant differently than you probably intended. – Nawaz Feb 10 '18 at 17:01
  • 4
    It isn't the most vexing parse. It simply isn't allowed by the language. – juanchopanza Feb 10 '18 at 17:12
  • @Nawaz: `std::vector v(3);` does not compile in C++11 when inside a class definition, as in `struct Foo` above. – xdavidliu Feb 10 '18 at 20:26
  • @xdavidliu: My question is, how is that most vexing parse? When did you get that from? – Nawaz Feb 11 '18 at 06:27
  • @Nawaz I was under the impression that `std::vector v(3);` does not compile when inside a class declaration because the compiler can't tell if we are trying to declare a member vector `v` or declare a function with name `v`. I was under the impression that this problem is known as "most vexing parse". Can you let me know what part of that is incorrect? – xdavidliu Feb 12 '18 at 00:09
  • @Nawaz, ah, so perhaps the issue is that most vexing parse would only apply if it said `std::vector v()` in which case it would *really* be vexing, and in this case since we have `3` inside the parentheses, the compiler ideally would have known in principle that we are not trying to declare a function, since `3` is obviously not a parameter. However, I think the reason why this code doesn't compile is still related to the concept of MVP, since the reason the two don't compile, e.g. a member declaration being confused for a function declaration, is essentially similar? – xdavidliu Feb 12 '18 at 00:34
  • @xdavidliu: "*since we have 3 inside the parentheses, the compiler ideally would have known in principle that we are not trying to declare a function, since 3 is obviously not a parameter*" .... Exactly! – Nawaz Feb 12 '18 at 05:31

1 Answers1

10

Default member initializers work with = as well. So

struct Foo{
    std::vector<int> v = std::vector<int>(3);
};

Will do it. Though obviously, a major caveat is the fact that we are repeating the type name here.

We can alleviate it somewhat with decltype:

struct Foo{
    std::vector<int> v = decltype(v)(3);
};

But that still has us naming things twice.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Hmm, I wonder why `auto` isn't allowed for members with an in-class initializer... Would make it a bit more readable. – DeiDei Feb 10 '18 at 17:21
  • @DeiDei - I always assumed it's because you can mix default member initializers with initialization in the c'tor as well as with aggregate initialization. In both cases, the default member initializer isn't gonna be used, so deducing the type from it is kinda weird. – StoryTeller - Unslander Monica Feb 10 '18 at 17:24
  • What about using a constructor member initialization list? `struct Foo { std::vector v; Foo() : v(3) {} };` – Remy Lebeau Feb 10 '18 at 17:29
  • 3
    @RemyLebeau - The OP mentioned it as the obvious solution they would go with. The focus of the post is if it's at all possible with a default member initializer. – StoryTeller - Unslander Monica Feb 10 '18 at 17:30
  • @ StoryTeller well, it isn't possible because what he calls initializer list isn't one. Latter is available only for trivially constructed data structures. – Swift - Friday Pie Feb 10 '18 at 18:35
  • 1
    @Swift - What isn't possible? I don't get what you are referring to – StoryTeller - Unslander Monica Feb 10 '18 at 18:37
  • @StoryTeller ah, this question is sequel of the line about fact that vector ctor that accepts initializer list disallows to use other ctor with types compatible with size_type used as vector's storage type, because initializer list and uniform initializers have same syntax. This "pun" is known as "uniform initializer that isn't".There is no workaround about it that allows same look as std::string initialization... unless OP would create a proxy template class derived from vector. – Swift - Friday Pie Feb 10 '18 at 18:44
  • 2
    The OP clearly asked how to use employ the desired c'tor of `std::vector`, given default member initializers don't accept round brackets `()`. To say it's not possible and without a workaround, given I quite clearly presented one is a rather odd thing to say. – StoryTeller - Unslander Monica Feb 10 '18 at 18:47
  • @StoryTeller "perform the desired initialization, as closely as possible to the first std::string example" I don't think that value initializer is closest variant. It's just a fall back to vanilla C++98 syntax, an acceptable one though. – Swift - Friday Pie Feb 10 '18 at 18:50
  • 3
    @Swift - You and I interpret "close" differently. I took it to mean as calling the same c'tor in the end (with some copy elision for seasoning). Not forcing a particular initializer to work like it can't isn't what I understood from the OP. – StoryTeller - Unslander Monica Feb 10 '18 at 18:52
  • @StoryTeller yeah, I know. I offered one in answer to his previous question, which fact strangely was ignored, now I think, you managed to convince him. What I think.. can we actually derive from std::vector and mask (delete) offending constructor? – Swift - Friday Pie Feb 10 '18 at 18:54
  • @Swift - We can, but it would be arduous. Simply inheriting all `vector`'s c'tors and deleting the offending one won't work. Since it would still be defined, and found in overload resolution. Instead we'd need to explicitly write each "forwarding" c'tor to `std::vector` by hand. – StoryTeller - Unslander Monica Feb 10 '18 at 18:59
  • @DeiDei Initializers are evaluated after the member declarations. If `auto` was allowed for member variables, it would be possible to write absurd code like `struct S{ auto a = decltype(b){}; auto b = decltype(a){}; };` – Arne Vogel Feb 11 '18 at 19:29