3

This is copy paste from this topic Initializing fields in constructor - initializer list vs constructor body

The author explains the following equivalence:

    public : Thing(int _foo, int _bar){
        member1 = _foo;
        member2 = _bar;
    }

is equivalent to

    public : Thing(int _foo, int _bar) : member1(), member2(){
        member1 = _foo;
        member2 = _bar;
    }

My understanding was that

  • snippet 1 is a case of default-initialization (because of the absence of an initializer list)
  • snippet 2 is a case of value-initialization (empty pairs of parentheses).

How are these two equivalent?

Community
  • 1
  • 1
usual me
  • 8,338
  • 10
  • 52
  • 95
  • 4
    The two are only equivalent for non-POD user defined types. For built-ins, default initialization means no initialization. – juanchopanza Oct 16 '13 at 15:17
  • 3
    @juanchopanza: Not all user-defined types, *only* non-pod types! – Nawaz Oct 16 '13 at 15:20
  • 1
    @Nawaz Good point. Fixed. – juanchopanza Oct 16 '13 at 15:22
  • @Nawaz [class.base.init]/8 says the members are default-initialized if there's no initializer in the *mem-initializer-list* (and no *brace-or-equal-initializer*), and [dcl.init]/7 says that default-initialization for *any* class type means calling the default ctor. Therefore, for a POD class, the default ctor will be called. – dyp Oct 16 '13 at 15:23
  • 2
    @DyP: A POD class can't contain a user-defined ctor, nor can anything it aggregates. Therefore, the default ctors for it and whatever it contains must be the sorts of ones that are basically NOPs (e.g., like `int`'s). – Jerry Coffin Oct 16 '13 at 15:27
  • @DyP: The implicitly defined default constructor for POD doesn't initialize the members. 12.1/6 says *"The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class **with no ctor-initializer** (12.6.2) and an empty compound-statement."* – Nawaz Oct 16 '13 at 15:30
  • Btw: You should remove the assignment in the second constructor. –  Oct 16 '13 at 15:30
  • 1
    @Nawaz It's not POD-ness which makes a difference; it's rather whether the class has a user defined constructor or not. (If a class has virtual functions, for example, it is not a POD, but the behavior in the two cases is still different.) – James Kanze Oct 16 '13 at 15:30
  • @JamesKanze: Yes, that is true. – Nawaz Oct 16 '13 at 15:32
  • @juanchopanza: Under the *as-if-rule* they can be equivalent for built-ins as well. (Please, see my answer and point out any mistake that I might have made.) – Cassio Neri Oct 16 '13 at 17:03

2 Answers2

3

Your understanding is correct (assuming member1 and member2 have type `int). The two forms are not equivalent; in the first, the members are not initialized at all, and cannot be used until they have been assigned. In the second case, the members will be initialized to 0. The two formulations are only equivalent if the members are class types with user defined constructors.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • "The two formulations are only equivalent if the members are class types with user defined constructors." And if the implicitly declared default ctor is deleted (making both versions ill-formed). – dyp Oct 16 '13 at 15:39
  • @DyP Or if it isn't accessible. Any time one of the two forms doesn't compile, the code is clearly not equivalent. – James Kanze Oct 16 '13 at 15:46
  • "Any time one of the two forms doesn't compile" Huh? What I meant was that *value-initialization* is defined as *default-initialization* in two cases: 1) If there's a user-provided default ctor 2) If the default ctor is deleted. – dyp Oct 16 '13 at 15:47
  • @DyP: Under the as-if-rule they can be equivalent for non class types as well. (Please, see my answer and point out any mistake that I might have made.) – Cassio Neri Oct 16 '13 at 17:10
  • @CassioNeri Could be. If you accidentally use them before initialization, you have undefined behavior, so the compiler can do anything it wants, including making the code work by moving the initialization up where it belongs. – James Kanze Oct 16 '13 at 17:26
  • @JamesKanze: "if you accidentally use them before initialization, you have undefined behavior". Not necessarilly. If they are of type `int`, for instance, their initial value is indeterminate (8.5/11) but the semantics of the program is well defined. So, even without undefined behavior, thanks to the as-if-rule, the two snippets might (but not necessarily will) be equivalent. The discussion also depends on the definition of "equivalent". I'm considering "generate the same code" as a definition of equivalent. – Cassio Neri Oct 17 '13 at 07:20
  • @CassioNeri If you use the value of an `int` before it has been initialized, you have undefined behavior. – James Kanze Oct 17 '13 at 08:27
1

You are right but the author is kind of right too!

Your interpretation is completely correct as are the answers given by others. In summary the two snippets are equivalent if member1 and member2 are non-POD types.

For certain POD types they are also equivalent in some sense. Well, let's simplify a little more and assume member1 and member2 have type int. Then, under the as-if-rule the complier is allowed to replace the second snippet with the first one. Indeed, in the second snippet the fact that member1 is first initlialized to 0 is not observable. Only its assignment to _foo is. This is the same reasoning that allows the compiler to replace these two lines

int x = 0;
x = 1;

with this one

int x = 1;

For instance, I've compiled this code

struct Thing {

    int member1, member2;

    __attribute__ ((noinline)) Thing(int _foo, int _bar)
        : member1(), member2() // initialization line
    {
        member1 = _foo;
        member2 = _bar;
    }
};

Thing dummy(255, 256);

with GCC 4.8.1 using option -O1. (The __atribute((noinline))__ prevents the compiler from inlining the function). Then the generated assembly code is the same regardless whether the initialization line is present or not:

-O1 with or without initialization

   0:   8b 44 24 04             mov    0x4(%esp),%eax
   4:   89 01                   mov    %eax,(%ecx)
   6:   8b 44 24 08             mov    0x8(%esp),%eax
   a:   89 41 04                mov    %eax,0x4(%ecx)
   d:   c2 08 00                ret    $0x8

On the other hand, when compiled with -O0 the assembly code is different depending on whether the initialization line is present or not:

-O0 without initialization

   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 04                sub    $0x4,%esp
   6:   89 4d fc                mov    %ecx,-0x4(%ebp)
   9:   8b 45 fc                mov    -0x4(%ebp),%eax
   c:   8b 55 08                mov    0x8(%ebp),%edx
   f:   89 10                   mov    %edx,(%eax)
  11:   8b 45 fc                mov    -0x4(%ebp),%eax
  14:   8b 55 0c                mov    0xc(%ebp),%edx
  17:   89 50 04                mov    %edx,0x4(%eax)
  1a:   c9                      leave  
  1b:   c2 08 00                ret    $0x8
  1e:   90                      nop
  1f:   90                      nop

-O0 with initialization

   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 04                sub    $0x4,%esp
   6:   89 4d fc                mov    %ecx,-0x4(%ebp)
   9:   8b 45 fc                mov    -0x4(%ebp),%eax   ; extra line #1
   c:   c7 00 00 00 00 00       movl   $0x0,(%eax)       ; extra line #2
  12:   8b 45 fc                mov    -0x4(%ebp),%eax   ; extra line #3
  15:   c7 40 04 00 00 00 00    movl   $0x0,0x4(%eax)    ; extra line #4
  1c:   8b 45 fc                mov    -0x4(%ebp),%eax
  1f:   8b 55 08                mov    0x8(%ebp),%edx
  22:   89 10                   mov    %edx,(%eax)
  24:   8b 45 fc                mov    -0x4(%ebp),%eax
  27:   8b 55 0c                mov    0xc(%ebp),%edx
  2a:   89 50 04                mov    %edx,0x4(%eax)
  2d:   c9                      leave  
  2e:   c2 08 00                ret    $0x8
  31:   90                      nop
  32:   90                      nop
  33:   90                      nop

Notice that -O0 with initialization has four extra lines (marked above) than -O0 without initialization. These extra lines initialize the two members to zero.

Cassio Neri
  • 19,583
  • 7
  • 46
  • 68
  • Even for non-POD class types they may yield the same code. However, they're semantically equivalent only if the member types don't have a user-provided default ctor, or if the default ctor is deleted. (That includes certain non-POD classes as well, see James Kanze's [comment to the OP](http://stackoverflow.com/questions/19407228/no-initializer-list-vs-initializer-list-with-empty-pairs-of-parentheses/19407534?noredirect=1#comment28767482_19407228).) – dyp Oct 16 '13 at 17:24