1

Why can't my constructor initializer use copy initialization?

struct S { int a; S(int b) : a(b)  {} }; // direct initialization compiles
struct T { int a; T(int b) : a = b {} }; // copy initialization does not

I am confused because a(b) and a = b are both expressions (postfix and assignent expressions, resp.), and my C++ book [1] says that "An initializer may be any arbitrarily complex expression."

[1] Lippman, Lajoie, Moo. "C++ Primer, 4th ed." p457.

nknight
  • 1,034
  • 8
  • 17

1 Answers1

3

That's not direct initialization. T a = b; is called copy intialization. Direct initialization is T a(1, 'foo', false);, and in your constructor you would write the familiar T(int b) : a(b, 'foo', false) { } to achieve this effect, as you already have in your first example.

By contrast, T a; is called default initialization, which you achieve by leaving a variable entirely unmentioned in the initializer list. Its effect is to call the default constructor on class types, and to perform no initialization at all on fundamental types (ditto for arrays).

By contrast again, value initialization can be written as T(int b) : a() { }. You can also use value-initialization in new expressions, but they're tricker in automatic declarations due to the vexing parse.

I think direct, default and value initialization are the only permissible forms of initialization in initializer lists in C++98/03, while C++11 adds various flavours of uniform initialization to the mix.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    `T a;` is _default_ initialization, not direct initialization. `T a(b);` is direct initialization. – ildjarn Nov 03 '11 at 21:29
  • "*no initialization at all on fundamental types (ditto for arrays)*" Or any POD-type. – ildjarn Nov 03 '11 at 21:45
  • @ildjarn: "defined recursively for members", I suppose? – Kerrek SB Nov 03 '11 at 21:47
  • Not sure what you mean -- a type can't be a POD-type if it contains non-POD members. – ildjarn Nov 03 '11 at 21:49
  • Let's see. If you initialize any object, then any member which is not mentioned in the CIL will be default-initialized, is that right? So then this proceeds recursively, until you hit a fundamental type, which is either default-initialized or not. I should be able to mix this happily with non-PODs; any given constructor along the way may choose to default-initialize one of its members. Or am I going wrong somewhere with this? – Kerrek SB Nov 03 '11 at 21:57
  • @Kerrick: Thanks for correcting my mix-up between direct and copy initialization. Any intuition on why the language won't allow copy initialization? Don't a(b) and a=b have identical semantics for POD types? (http://stackoverflow.com/questions/1051379/is-there-a-difference-in-c-between-copy-initialization-and-direct-initializati/1051468#1051468) – nknight Nov 03 '11 at 22:10
  • 1
    @nknight: What would be the point? Direct-initialization is already good enough to invoke the copy or move constructor; what else do you need? Copy-initialization is really the most dubious feature of the language that probably only exists for cosmetic reasons... – Kerrek SB Nov 03 '11 at 22:16
  • "*If you initialize any object, then any member which is not mentioned in the CIL will be default-initialized, is that right?*" Yes, right. "*So then this proceeds recursively, until you hit a fundamental type, which is either default-initialized or not.*" Until you hit a POD-type, not just a fundamental type. Default-initialization of a POD-type == no initialization, including of that POD-type's submembers. – ildjarn Nov 03 '11 at 22:19
  • @ildjarn: Fair enough. But that's physically indistinguishable to the POD type having an empty default constructor that default-initializes its members, isn't it? Or is there an explicit difference? If there's a difference, then I suppose that would distinguish PODs from aggregates. – Kerrek SB Nov 03 '11 at 22:21
  • A POD type cannot have any constructor (otherwise it wouldn't be a POD type), so the question doesn't make much sense. :-P – ildjarn Nov 03 '11 at 22:23
  • Ah: Now this is very hair-splitty, but for the sake of argument: Can I interpret that to mean that I mustn't specify a *user-defined* constructor, and thus that the object will get a default-provided, empty constructor which does nothing? Or is there an explicit statement that there is no constructor at all? It still seems to me like the two are indistinguishable, but I'm interested if there's anything specific in the standard. – Kerrek SB Nov 03 '11 at 22:25
  • There is a _trivial_ default constructor that does nothing. The standardese is in the FDIS, §12.1. – ildjarn Nov 03 '11 at 22:26
  • @Kerrick: I completely agree with your practical advice that direct initialization is "already good enough" in a constructor initializer list. I have not accepted an answer yet because my question seeks to uncover (a) why my example leads to a syntax error, and (b) why this syntax rule exists for C++. – nknight Nov 03 '11 at 22:47
  • Well, (a) because copy-initialization isn't allowed in the initializer list according to the standard (12.6.2.1 in C++11), and (b) is a subjective question about the design -- personally I'm all in favour of it, and I tend to understand the CIL as "the list of constructor calls to use when constructing the subobjects and members". As such I only want explicit constructor calls. Note that copy-initialization requires an accessible and *non-explicit* copy constructor! – Kerrek SB Nov 03 '11 at 22:49
  • @nknight : The latter is not a question answerable on SO, as we don't know the language designers' intents. The best you can be told here is the correct syntax to use, which Kerrek has already done. – ildjarn Nov 03 '11 at 22:50
  • @ildjarn: Now I remember why I went into this extended discussion of ours: I was wondering whether I could simply remove the notion of POD entirely for the purpose of initialization and replace it by existing rules. Just a silly game for finding a minimal set of axioms, if you like -- of course knowing about PODs is very useful in practice! – Kerrek SB Nov 03 '11 at 22:53
  • @Kerrek : I'm not sure what you mean -- the existing rules are oriented around POD or non-PODness, so how could one remove the notion of POD entirely? If you mean to say that POD types will be treated as fundamental types in regards to they way they're default-initialized, then that's true, but only because it's backwards -- fundamental (scalar) types are POD types. – ildjarn Nov 03 '11 at 22:55
  • @ildjarn: No, rather I'd like to say that POD types are just classes with trivial constructors (for the purpose of initialization), and apply the rules recursively -- makes sense? – Kerrek SB Nov 03 '11 at 22:56
  • @Kerrek : But there's more to it than that, as always ;-] A POD type (in C++11) must have a trivial default constructor, must be trivially copyable, must not have virtual functions or virtual base classes, must have the same access control for all non-static data members, and must only contain subobjects that also meet these same criteria, etc. A class with a trivial default constructor is just that -- it takes much more than that to qualify as a POD type. – ildjarn Nov 03 '11 at 23:00
  • @ildjarn: Yes yes, I know... but here I mean just for the purpose of "how are members initialized": If I know that the class has a trivial constructor (and copy constructor), I already know everything I need to about the member initialization. So my question is if I still need to treat POD specially for the purpose of initialization, or can I just use the rule that "trivial constructor causes members to be default-initialized". – Kerrek SB Nov 03 '11 at 23:03
  • @Kerrek : Ah, okay, I think we're finally on the same page. :-] For your purposes (if you're using C++11 terminology) then yes, types possessing a trivial default constructor are the ones to single out in regards to default-initialization. Of course for C++03, POD-type is still the noteworthy criteria. – ildjarn Nov 03 '11 at 23:06
  • @ildjarn: Awesome. What's the difference in C++03 though? Does it not have "trivial constructors"? (I also didn't realize that value initialization was only added in 03; there may/will be many further gaps in my knowledge!) – Kerrek SB Nov 03 '11 at 23:07
  • @Kerrek : C++11 separated the concept of a POD-type into separate classifications: having a _trivial default constructor_, being _trivially copyable_, and being _standard layout_. In C++03 these were not separate concepts, and all three were thrown together to define 'POD-type'. (And yes, C++03's largest changes were the concept of value-initialization and requiring that `vector<>` have contiguous storage -- important changes indeed!) – ildjarn Nov 03 '11 at 23:12
  • @Kerrek: Thanks for the reference to the spec. It appears that the mem-initializer must use a parentheses-based initializer (direct or value, like you said). As for my subjective question (b), I was curious if you knew any deep reason for this rule, because it appears I can superficially subvert it with something perverse like `struct U { int a, b; U (int c) : a(b = c) {} };` – nknight Nov 03 '11 at 23:46
  • @nknight: No you can't: Elements are initialized in their order of declaration, and you cannot use `b` before it is initialized. Don't cheat! :-) Assignment always has to come after initialization, and in constructors never if you can help it. – Kerrek SB Nov 03 '11 at 23:51
  • @Kerreck: How come that code snippet compiles and runs without error or warning and initializes both U.a and U.b correctly (G++ 4.5.3)? Is this behavior implementation-defined? – nknight Nov 03 '11 at 23:55
  • @nknight: I'd say that's a compiler shortcoming. You're using `b` before it is initialized, since we're still in the process of initializing `a`. I'd like to ask this as a separate question; shall I do it or do you want it? – Kerrek SB Nov 04 '11 at 00:01
  • Thank you both for your insightful discussions and for patiently answering my first SO question! :) – nknight Nov 04 '11 at 01:28
  • @nknight : In C++11, you can use brace-based initializers in addition to parenthesis-based initializers. This C++11 addition is generally referred to as 'Uniform Initialization'. – ildjarn Nov 04 '11 at 17:15
  • @ildjarn: yay for uniform initialization! It's a great, awesome new feature (hello static constant maps!), as well as an ongoing source of GCC internal compiler errors :-) – Kerrek SB Nov 04 '11 at 17:20