Default constructor weirdness in member initialization list

M

mhvaughn

struct S1
{
int i;
};

struct S2 {
S1 s;
// version 1
S2() {} ;
// version 2
S2() : s() {} ;
};

int main()
{
S2 s;
printf( "%d\n", s.s.i );
return 0;
}

----------

With version 1 of the S2 constructor the program will spew random
values as expected.
With version 2 of the S2 constructor the program only spews zeros.
Does explicitly calling the default constructor in the member
initialization list somehow cause the "default" constructor for the
int in S1 to be called?
 
A

Alf P. Steinbach

* (e-mail address removed):
struct S1
{
int i;
};

struct S2 {
S1 s;
// version 1
S2() {} ;
// version 2
S2() : s() {} ;
};

int main()
{
S2 s;
printf( "%d\n", s.s.i );
return 0;
}

----------

With version 1 of the S2 constructor the program will spew random
values as expected.
With version 2 of the S2 constructor the program only spews zeros.
Does explicitly calling the default constructor in the member
initialization list somehow cause the "default" constructor for the
int in S1 to be called?

No, an int does not have a constructor: the initialization syntax just
resembles an explicit constructor call.

Since you initialize the member it is initialized.

In standard C++ this initialization syntax also works for an array
member, although compiler support for that varies.

Cheers, & hth

- Alf
 
D

devp

struct S1
{
int i;

};

struct S2 {
S1 s;
// version 1
S2() {} ;
// version 2
S2() : s() {} ;

};

int main()
{
S2 s;
printf( "%d\n", s.s.i );
return 0;

}

----------

With version 1 of the S2 constructor the program will spew random
values as expected.
With version 2 of the S2 constructor the program only spews zeros.
Does explicitly calling the default constructor in the member
initialization list somehow cause the "default" constructor for the
int in S1 to be called?

Hi
I tried the program with g++ it works same both ways, as should. Which
compiler did you use?

cheers
projyal
 
A

Alf P. Steinbach

* devp:
Hi
I tried the program with g++ it works same both ways, as should.

Version 1 has undefined behavior.

Undefined behavior includes doing what one incorrectly might expect.

Which compiler did you use?

Different compilers, versions of the same compiler, and invocations of
the same version of the same compiler but with different options, often
yield different results for undefined behavior.

Cheers, & hth.,

- Alf
 
M

mhvaughn

* (e-mail address removed):






No, an int does not have a constructor: the initialization syntax just
resembles an explicit constructor call.

Since you initialize the member it is initialized.

But the member I initialized is of type S1. Since there is no
constructor provided for S1 the compiler should provide one which
should do nothing. I'm perfectly willing to accept that it just seems
like its doing something, I guess I just wanted to double-check that
default constructors in member initialization lists are not special in
some way (e.g., like global structs whose members are zeroed out when
created).
 
M

mhvaughn

Hi
I tried the program with g++ it works same both ways, as should. Which
compiler did you use?

[615]$g++ -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --enable-languages=c,c+
+,fortran,objc,obj-c++,treelang --prefix=/usr --enable-shared --with-
system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-
threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/
4.1.3 --program-suffix=-4.1 --enable-__cxa_atexit --enable-clocale=gnu
--enable-libstdcxx-debug --enable-mpfr --enable-checking=release
x86_64-linux-gnu
Thread model: posix
gcc version 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)
 
A

Alf P. Steinbach

* (e-mail address removed):
But the member I initialized is of type S1.

Previous comment still applies, but to be honest, I didn't notice...

Since there is no
constructor provided for S1 the compiler should provide one which
should do nothing.

If it's used (ref. §12.1/7).

But, the language being as backwards in certain respects as it is, to
use that do-nothing automatic default constructor you have to /not/
indicate that you want it, e.g. for a local variable,

S1 o; // Do-nothing auto default constructor used, §12.1/8.

If you try to add parentheses to say you want to use the darned thing,
then in the C++ "do the opposite of what's expected"-way it isn't used:

o = S1(); // Not an invocation of the do-nothing def. constructor.

What happens here is according to C++98 a zero-initialization (§8.5/7,
§8.5/5) and IIRC according to C++03 a value-initialization, essentially
zeroing or default-initializing members depending on whether they have
user-defined default constructors or not. And the same applies to your
code above, in the second version. So the int is zeroed, and that
zeroing does not involve a constructor, not even of S1.

I'm perfectly willing to accept that it just seems
like its doing something, I guess I just wanted to double-check that
default constructors in member initialization lists are not special in
some way (e.g., like global structs whose members are zeroed out when
created).

You're not using the S1 default constructor; see above.


Cheers, & hth.,

- Alf
 
A

Andrey Tarasevich

struct S1
{
int i;
};

struct S2 {
S1 s;
// version 1
S2() {} ;
// version 2
S2() : s() {} ;
};

int main()
{
S2 s;
printf( "%d\n", s.s.i );
return 0;
}

----------

With version 1 of the S2 constructor the program will spew random
values as expected.
OK.

With version 2 of the S2 constructor the program only spews zeros.
Yes.

Does explicitly calling the default constructor in the member
initialization list somehow cause the "default" constructor for the
int in S1 to be called?

Firstly, you are not "explicitly calling" the default constructor of S1
by using 's()' syntax in the constructor initializer list. In C++
there's no syntax and way in general to "explicitly call" a constructor.

Secondly, only obects of class types can have constructors in C++.
Objects of type 'int' (as 'S1::i' in your case) have no construtors
whatsoever.

Now, when you specify a '()' initializer for an object, it causes so
called "value-initialization" of that object. Note, that the notion of
"initialization" in C++ is more general that the notion of
"construction". Initialization does not necessarily involve construction
(and constructors). For an object of class type without any user-defined
constructors (like 's' of type 'S1' in your example),
value-initialization does not involve any constructors of S1.
Value-initialization for an object of type S1 results in
value-initialization of all of its subobjects. In this case it is
value-initialization if 'S1::i' member of type 'int'. For type 'int'
value-initialization means zero-initialization. I.e. 'S1::i' is set to
zero. It is done directly, without involving any constructors.

Note once again, that the only constructor that is involved in this
process is the constructor of 'S2'. There are no more constructors
involved here. 'S1' and its member 'S1::i' are initialized directly.
 
A

Alf P. Steinbach

* Andrey Tarasevich:
Firstly, you are not "explicitly calling" the default constructor of S1
by using 's()' syntax in the constructor initializer list.
Right.


In C++
there's no syntax and way in general to "explicitly call" a constructor.

I'm sorry, that's incorrect.

There's no dedicated C++ syntax for constructor calls. What the syntax
means depends on context. Whether to say that an explicit constructor
call is an explicit constructor call is a terminologial issue.

The language's creator calls an explicit constructor call an explicit
constructor call. Some programmers who post in this newsgroup have
taken issue with that, insisting that the word "call" is inappropriate
for source code level constructor calls, thus rendering e.g. the
standard's definition of default constructor (what we're discussing
here) meaningless. But to each his own, just don't propagate the myth
that that terminological stance has any substance. :)


Cheers, & hth.,

- Alf
 
A

Andrey Tarasevich

Alf said:
...

I'm sorry, that's incorrect.

There's no dedicated C++ syntax for constructor calls. What the syntax
means depends on context. Whether to say that an explicit constructor
call is an explicit constructor call is a terminologial issue.

I disagree. Constructors are functions. They are special functions, but
functions nevertheless. For this reason I'd expect the "explicit
constructor call", if it existed, to be have the function call
semantics. However, if I'm not mistaken, there's no way to call
constructor in C++ by using function call semantics. Constructors calls
in C++ are "side effects" of the more generic process called
"initialization". It is is invoked by various language constructs
having... well, initialization semantics, which is essentially different
from function call semantics. This is why I say that there's no way to
"call" a constructor in C++.
The language's creator calls an explicit constructor call an explicit
constructor call.

Where? In TC++PL? If yes, then sorry, but that book is not canonical in
the context of ANSI standard C++ discussion. It is intentionally
simplified to be more accessible for newcomers. And the price of that
simplification is that sometimes it makes statements, which are simply
incorrect from the standard C++ point of view. (For example, it states
that scalar types have constructors, if I remember correctly).
 
A

Alf P. Steinbach

* Andrey Tarasevich:
I disagree. Constructors are functions. They are special functions, but
functions nevertheless. For this reason I'd expect the "explicit
constructor call", if it existed, to be have the function call
semantics.

Notwithstanding the impracticality of adopting a terminology that
excludes comprehension of crucial parts of the standard, the above is a
circular argument, defining "call" as any function call that is not a
source level constructor call, then maintaining that that in some sense
means that "call" is unsuitable for source level constructor calls.

If you try to define what you think you mean non-circularly you run into
problems with e.g. explicit destructor calls (semantics) and implicit
type conversion operator calls (syntax).

This is the same kind of problem that people run into when they try to
define religious beliefs. A Canadian pilot who tried to communicate
with God -- in-flight -- was placed in irons last week, and sent to
hospital when the plane landed, whereas to become pilot of the United
States of America you have to insist on believing in that kind of thing
and demonstrate that you practice... The only way out while maintaining
such belief is to steadfastly ignore reality, to double-think.

Where? In TC++PL?

E.g.

Andrew Koenig & Bjarne Stroustrup:
"Foundations for Native C++ Styles"
<url:
http://parasol.tamu.edu/people/rwerger/Courses/434/c++primer.ps>.

"The explicit constructor call greater<int>() will serve the same
purpose by creating an anonymous object:"

Might throw in Nicola Josuttis too if you want more. :)

If yes, then sorry, but that book is not canonical in
the context of ANSI standard C++ discussion. It is intentionally
simplified to be more accessible for newcomers. And the price of that
simplification is that sometimes it makes statements, which are simply
incorrect from the standard C++ point of view. (For example, it states
that scalar types have constructors, if I remember correctly).

See above.


Cheers, & hth.,

- Alf
 
J

James Kanze

I disagree. Constructors are functions. They are special
functions, but functions nevertheless. For this reason I'd
expect the "explicit constructor call", if it existed, to be
have the function call semantics. However, if I'm not
mistaken, there's no way to call constructor in C++ by using
function call semantics. Constructors calls in C++ are "side
effects" of the more generic process called "initialization".

More importantly, every way you can "call" a constructor
involves memory allocation.
It is is invoked by various language constructs having...
well, initialization semantics, which is essentially different
from function call semantics. This is why I say that there's
no way to "call" a constructor in C++.

It depends on the level. One of the syntax's for the explicit
creation of a temporary object "looks" like a function call,
with the name of the function being that of the class (which is
the name you use to declare a constructor). So traditionally, a
lot of people have informally called it an explicit constructor
call. The standard calls it an "explicit type conversion
(functional notation)", which is 100% accurate when there is
only one argument, but doesn't really make sense otherwise. And
of course, there are lots of ways to "call" a constructor---the
only question is whether any of them merit the name "explicit".
Where? In TC++PL? If yes, then sorry, but that book is not
canonical in the context of ANSI standard C++ discussion. It
is intentionally simplified to be more accessible for
newcomers. And the price of that simplification is that
sometimes it makes statements, which are simply incorrect from
the standard C++ point of view. (For example, it states that
scalar types have constructors, if I remember correctly).

I think that there are even one or two places in the standard
where the expression "explicit constructor call" occurs.
Regretfully, the standard doesn't define what it means by that,
so we're still left guessing. I don't like the term "explicit
constructor call" because for most people I know, it immediately
calls to mind the "explicit type conversion (functional
notation)" of the standard, and that, of course, doesn't
necessarily result in a constructor call at all. I don't like
the term used in the standard for this either, since I find it
hard to call something like "MyClass()" or "MyClass(a, b)" a
type conversion---what type is being converted. So I resort to
"explicit creation of a temporary". Similarly, I'll find other
expressions for the other cases where I end up calling a
constructor: a placement new expression, for example (which is
semantically the closest you can come to really "calling the
constructor", but syntactically, of course, is a new
expression).
 
M

mhvaughn

* (e-mail address removed):





Previous comment still applies, but to be honest, I didn't notice...


If it's used (ref. §12.1/7).

But, the language being as backwards in certain respects as it is, to
use that do-nothing automatic default constructor you have to /not/
indicate that you want it, e.g. for a local variable,

S1 o; // Do-nothing auto default constructor used, §12.1/8.

If you try to add parentheses to say you want to use the darned thing,
then in the C++ "do the opposite of what's expected"-way it isn't used:

o = S1(); // Not an invocation of the do-nothing def. constructor.

What happens here is according to C++98 a zero-initialization(§8.5/7,
§8.5/5) and IIRC according to C++03 a value-initialization, essentially
zeroing or default-initializing members depending on whether they have
user-defined default constructors or not. And the same applies to your
code above, in the second version. So the int is zeroed, and that
zeroing does not involve a constructor, not even of S1.


You're not using the S1 default constructor; see above.

Cheers, & hth.,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

First off, thanks very much for the explanation... that behavior is
unintuitive (to me) but good to know about.

I've been playing around with bits of code related to implicit
creation of default constructors and so far I've been able to relate
code behavior to the description in the standard except for the
following:

#include <stdio.h>

// struct S1 { int j; };
struct S1 { int j; S1 () {}; };
struct S2 { int i; S1 s1; };

int main()
{
S2 s2 = S2();
printf( "%d,%d\n", s2.i, s2.s1.j );
return 0;
}

There is no user-defined default constructor for S2 so an implicit
constructor is automatically declared by the compiler.
The first line of main creates an object with an initializer so the
compiler doesn't define that default constructor.
So the s2 struct is default-initialized but (since it has no defined
constructor) is zero-initialized.
Why, then, is s2.i undefined?
Also, is there some part of the standard which clearly indicates when
the constructor for S1 gets called?
 
A

Alf P. Steinbach

* (e-mail address removed):[snip quoted signature, please don't quote signatures]
> I've been playing around with bits of code related to implicit
creation of default constructors and so far I've been able to relate
code behavior to the description in the standard except for the
following:

#include <stdio.h>

// struct S1 { int j; };
struct S1 { int j; S1 () {}; };
struct S2 { int i; S1 s1; };

int main()
{
S2 s2 = S2();
printf( "%d,%d\n", s2.i, s2.s1.j );
return 0;
}

There is no user-defined default constructor for S2 so an implicit
constructor is automatically declared by the compiler.
The first line of main creates an object with an initializer so the
compiler doesn't define that default constructor.
So the s2 struct is default-initialized but (since it has no defined
constructor) is zero-initialized.

The rules here are slightly different in C++98 (the original standard)
and C++03 (first technical corrigendum, the TC1).

C++03 introduced the notion of "value initialization" in order to tackle
just this problem, a correction of the C++98 semantics.

With C++98 semantics what happens is, as you state, a
default-initialization. And the problem with that is that the
definition of default-initialization, in §8.5./5, starts with "if T is a
non-POD class type, the default constructor for T is called". And in
the example above S2 is non-POD[1] (not a plain old C-like data type)
because it contains a member of a type with a defined constructor, which
is not something possible in C, and so according to C++98 semantics you
just get a call of your do-nothing auto-created default constructor.

With C++03 semantics what happens is a value initialization, intended to
correct this problem. The wording of value initialization starts with
"if T is a class type with a user-declared constructor, then the default
constructor for T is called". This doesn't apply to S2, which doesn't
have a user-declared constructor. But the next alternative, "if T is a
non-union class type without a user-declared constructor, then every
non-static data member and base-class component of T is
value-initialized", applies, recursively for S2::i and S2::s1. And for
S2::i the last alternative of this list applies, zero-initializion, and
for S2::s1 the first alternative applies, default constructor called.

So essentialy the compiler or compilers you have tested with evidently
implemented C++98 semantics, or perhaps just incorrect initialization,
not C++03 semantics. One remedy is to upgrade the compiler(s). Another
more general solution is to simply define a default constructor.

Cheers, & hth.,

- Alf


Notes:
[1] Technically it is an "aggregate" type.
 
M

mhvaughn

* (e-mail address removed):

[snip quoted signature, please don't quote signatures]
I've been playing around with bits of code related to implicit


creation of default constructors and so far I've been able to relate
code behavior to the description in the standard except for the
following:
#include <stdio.h>
// struct S1 { int j; };
struct S1 { int j; S1 () {}; };
struct S2 { int i; S1 s1; };
int main()
{
S2 s2 = S2();
printf( "%d,%d\n", s2.i, s2.s1.j );
return 0;
}
There is no user-defined default constructor for S2 so an implicit
constructor is automatically declared by the compiler.
The first line of main creates an object with an initializer so the
compiler doesn't define that default constructor.
So the s2 struct is default-initialized but (since it has no defined
constructor) is zero-initialized.

The rules here are slightly different in C++98 (the original standard)
and C++03 (first technical corrigendum, the TC1).

C++03 introduced the notion of "valueinitialization" in order to tackle
just this problem, a correction of the C++98 semantics.

With C++98 semantics what happens is, as you state, a
default-initialization. And the problem with that is that the
definition of default-initialization, in §8.5./5, starts with "if T is a
non-POD class type, the default constructor for T is called". And in
the example above S2 is non-POD[1] (not a plain old C-like data type)
because it contains amemberof a type with a defined constructor, which
is not something possible in C, and so according to C++98 semantics you
just get a call of your do-nothing auto-created default constructor.

With C++03 semantics what happens is a valueinitialization, intended to
correct this problem. The wording of valueinitializationstarts with
"if T is a class type with a user-declared constructor, then the default
constructor for T is called". This doesn't apply to S2, which doesn't
have a user-declared constructor. But the next alternative, "if T is a
non-union class type without a user-declared constructor, then every
non-static datamemberand base-class component of T is
value-initialized", applies, recursively for S2::i and S2::s1. And for
S2::i the last alternative of this list applies, zero-initializion, and
for S2::s1 the first alternative applies, default constructor called.

So essentialy the compiler or compilers you have tested with evidently
implemented C++98 semantics, or perhaps just incorrectinitialization,
not C++03 semantics. One remedy is to upgrade the compiler(s). Another
more general solution is to simply define a default constructor.

Cheers, & hth.,

- Alf

Notes:
[1] Technically it is an "aggregate" type.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Thanks very much. I think it's all clear now... turns out I was
confused on the definition of POD/nonPOD.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,124
Latest member
JuniorPell
Top