Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c++ > #82970 > unrolled thread
| Started by | Juha Nieminen <nospam@thanks.invalid> |
|---|---|
| First post | 2022-02-08 10:31 +0000 |
| Last post | 2022-02-09 07:26 +0000 |
| Articles | 6 — 4 participants |
Back to article view | Back to comp.lang.c++
Understanding default member initializers of constructorless structs Juha Nieminen <nospam@thanks.invalid> - 2022-02-08 10:31 +0000
Re: Understanding default member initializers of constructorless structs "Alf P. Steinbach" <alf.p.steinbach@gmail.com> - 2022-02-08 11:55 +0100
Re: Understanding default member initializers of constructorless structs Juha Nieminen <nospam@thanks.invalid> - 2022-02-08 12:37 +0000
Re: Understanding default member initializers of constructorless structs red floyd <no.spam.here@its.invalid> - 2022-02-08 10:56 -0800
Re: Understanding default member initializers of constructorless structs Andrey Tarasevich <andreytarasevich@hotmail.com> - 2022-02-08 07:29 -0800
Re: Understanding default member initializers of constructorless structs Juha Nieminen <nospam@thanks.invalid> - 2022-02-09 07:26 +0000
| From | Juha Nieminen <nospam@thanks.invalid> |
|---|---|
| Date | 2022-02-08 10:31 +0000 |
| Subject | Understanding default member initializers of constructorless structs |
| Message-ID | <sttgpr$1cud$1@gioia.aioe.org> |
I have always understood that the default member initializers (introduced
in C++11) merely added that particular initialization value to the
initialization list of a constructor, if it's not explicitly specified
there. In other words, if you do this:
struct A
{
int a = 5, b = 10;
A() {}
A(int v): a(v) {}
};
it results in pretty much the exact equivalent of:
struct A
{
int a, b;
A(): a(5), b(10) {}
A(int v): a(v), b(10) {}
};
The description at https://en.cppreference.com/w/cpp/language/data_members
seems to corroborate this notion. It says there:
Non-static data members may be initialized in one of two ways:
...
2) Through a default member initializer, which is a brace or equals
initializer included in the member declaration and is used if the
member is omitted from the member initializer list of a constructor.
I can't find any other explanation of the meaning and behavior of default
member initializers. (If another behavior is explained somewhere, I can't
find it.)
All the above is find and dandy, except that the meaning of default member
initializers seems to be rather different with constructorless structs
(and classes, I suppose). Consider this:
//---------------------------------------------------------------------------
#include <iostream>
struct S1 { int a = 5, b = 15, c = 25; };
struct S2 { int a, b, c = 35; };
struct S3 { int a = 1, b, c = 3; };
int main()
{
S1 s1 = { 100 };
S2 s2 = { 200, 300 };
S3 s3 = { 400 };
std::cout << "s1 = { " << s1.a << ", " << s1.b << ", " << s1.c << " }\n"
<< "s2 = { " << s2.a << ", " << s2.b << ", " << s2.c << " }\n"
<< "s3 = { " << s3.a << ", " << s3.b << ", " << s3.c << " }\n";
}
//---------------------------------------------------------------------------
It behaves as you would expect it to behave. The members not specified in
the initialization of the variables get the specified default values. So
this is printed:
s1 = { 100, 15, 25 }
s2 = { 200, 300, 35 }
s3 = { 400, 0, 3 }
But how does that work, exactly? The structs have no user-declared
constructors, so where exactly are those default values being "put",
so to speak? How are the objects constructed from the initializater
lists? Does the compiler, like, create an implicitly constructor
for those structs, with three int parameters with the specified
default values? In other words, is for example S1 above equivalent to:
struct S1
{
int a, b, c;
S1(int ia = 5, int ib = 15, int ic = 25):
a(ia), b(ib), c(ic) {}
};
I think it can't be *completely* equivalent to that because gcc,
if you specify -Wextra, will issue a warning from the line:
S3 s3 = { 400 };
warning: missing initializer for member 'S3::b'
s3.b will be initialized to 0 (IIRC this is guaranteed), but gcc
will still give a warning (if enough warning flags are turned on).
[toc] | [next] | [standalone]
| From | "Alf P. Steinbach" <alf.p.steinbach@gmail.com> |
|---|---|
| Date | 2022-02-08 11:55 +0100 |
| Message-ID | <stti72$hp4$1@dont-email.me> |
| In reply to | #82970 |
On 8 Feb 2022 11:31, Juha Nieminen wrote:
> I have always understood that the default member initializers (introduced
> in C++11) merely added that particular initialization value to the
> initialization list of a constructor, if it's not explicitly specified
> there. In other words, if you do this:
>
> struct A
> {
> int a = 5, b = 10;
>
> A() {}
> A(int v): a(v) {}
> };
>
> it results in pretty much the exact equivalent of:
>
> struct A
> {
> int a, b;
>
> A(): a(5), b(10) {}
> A(int v): a(v), b(10) {}
> };
>
> The description at https://en.cppreference.com/w/cpp/language/data_members
> seems to corroborate this notion. It says there:
>
> Non-static data members may be initialized in one of two ways:
> ...
> 2) Through a default member initializer, which is a brace or equals
> initializer included in the member declaration and is used if the
> member is omitted from the member initializer list of a constructor.
>
> I can't find any other explanation of the meaning and behavior of default
> member initializers. (If another behavior is explained somewhere, I can't
> find it.)
>
> All the above is find and dandy, except that the meaning of default member
> initializers seems to be rather different with constructorless structs
> (and classes, I suppose). Consider this:
>
> //---------------------------------------------------------------------------
> #include <iostream>
>
> struct S1 { int a = 5, b = 15, c = 25; };
> struct S2 { int a, b, c = 35; };
> struct S3 { int a = 1, b, c = 3; };
>
> int main()
> {
> S1 s1 = { 100 };
> S2 s2 = { 200, 300 };
> S3 s3 = { 400 };
>
> std::cout << "s1 = { " << s1.a << ", " << s1.b << ", " << s1.c << " }\n"
> << "s2 = { " << s2.a << ", " << s2.b << ", " << s2.c << " }\n"
> << "s3 = { " << s3.a << ", " << s3.b << ", " << s3.c << " }\n";
> }
> //---------------------------------------------------------------------------
>
> It behaves as you would expect it to behave. The members not specified in
> the initialization of the variables get the specified default values. So
> this is printed:
>
> s1 = { 100, 15, 25 }
> s2 = { 200, 300, 35 }
> s3 = { 400, 0, 3 }
>
> But how does that work, exactly? The structs have no user-declared
> constructors, so where exactly are those default values being "put",
> so to speak?
I believe but you'll have to check this, that there is a compiler
generated constructor.
Note: there was some change regarding what's regarded as an aggregate
class, an extension I think in C++17, sort of just to baffle people and
screw up old code.
> How are the objects constructed from the initializater
> lists? Does the compiler, like, create an implicitly constructor
> for those structs, with three int parameters with the specified
> default values? In other words, is for example S1 above equivalent to:
>
> struct S1
> {
> int a, b, c;
>
> S1(int ia = 5, int ib = 15, int ic = 25):
> a(ia), b(ib), c(ic) {}
> };
>
> I think it can't be *completely* equivalent to that because gcc,
> if you specify -Wextra, will issue a warning from the line:
>
> S3 s3 = { 400 };
>
> warning: missing initializer for member 'S3::b'
>
> s3.b will be initialized to 0 (IIRC this is guaranteed), but gcc
> will still give a warning (if enough warning flags are turned on).
It looks like a warning without name, so can't be turned off?
- Alf
[toc] | [prev] | [next] | [standalone]
| From | Juha Nieminen <nospam@thanks.invalid> |
|---|---|
| Date | 2022-02-08 12:37 +0000 |
| Message-ID | <stto70$106i$1@gioia.aioe.org> |
| In reply to | #82971 |
Alf P. Steinbach <alf.p.steinbach@gmail.com> wrote:
>> warning: missing initializer for member 'S3::b'
>
> It looks like a warning without name, so can't be turned off?
No, I just copied the relevant part of the warning for brevity. The full
line is:
test.cc:12:19: warning: missing initializer for member 'S3::b' [-Wmissing-field-initializers]
12 | S3 s3 = { 400 };
| ^
[toc] | [prev] | [next] | [standalone]
| From | red floyd <no.spam.here@its.invalid> |
|---|---|
| Date | 2022-02-08 10:56 -0800 |
| Message-ID | <stuecp$nib$1@redfloyd.dont-email.me> |
| In reply to | #82972 |
On 2/8/2022 4:37 AM, Juha Nieminen wrote:
> Alf P. Steinbach <alf.p.steinbach@gmail.com> wrote:
>>> warning: missing initializer for member 'S3::b'
>>
>> It looks like a warning without name, so can't be turned off?
>
> No, I just copied the relevant part of the warning for brevity. The full
> line is:
>
> test.cc:12:19: warning: missing initializer for member 'S3::b' [-Wmissing-field-initializers]
> 12 | S3 s3 = { 400 };
> | ^
I think this is correct. I believe it's the C-style aggregate
initialization (inherited from C90).
But because you don't initialize all members of s3, it uses the default
initializers for the second and third members of s3. It's also
providing a (non-mandatory) diagnostic letting you know what it's doing,
but that it may be a mistake.
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2022-02-08 07:29 -0800 |
| Message-ID | <stu29c$v3$1@dont-email.me> |
| In reply to | #82970 |
On 2/8/2022 2:31 AM, Juha Nieminen wrote:
> I have always understood that the default member initializers (introduced
> in C++11) merely added that particular initialization value to the
> initialization list of a constructor, if it's not explicitly specified
> there.
Yes, that was the case in C++11. And that was (and is) the initial
driving idea behind default initializers.
However, in C++11 the accompanying side-effect of this was that
specifying default initializers for class members immediately killed
"aggregateness" of the class. In C++11 a class with default initializers
would support `{}` initializer, but reject non-empty `{ ... }`
initializers (unless an appropriate user-declared constructor is present).
In C++11:
struct S
{
int a;
int x = 42;
};
int main()
{
S s1 = {}; // OK
S s2 = { 1 }; // ERROR: no such constructor
S s3 = { 2, 3 }; // ERROR: no such constructor
}
> All the above is find and dandy, except that the meaning of default member
> initializers seems to be rather different with constructorless structs
> (and classes, I suppose). Consider this:
The above "aggregate defying" behavior of default initializers was
reconsidered in C++14. Starting from C++14 a class with default
initializers does not lose its aggregate status and continues to support
aggregate initialization. And in C++14 aggregate initialization was also
made aware of default initializers.
> But how does that work, exactly? The structs have no user-declared
> constructors, so where exactly are those default values being "put",
> so to speak? How are the objects constructed from the initializater
> lists?
Aggregate initialization in C++ never used any "constructors" at the top
level. It is a separate built-in initialization mechanism with its own
custom specification. So, in C++14 this specification was simply
modified to take into account default initializers:
8.5.1 Aggregates
7 If there are fewer initializer-clauses in the list than there are
members in the aggregate, then each member not explicitly initialized
shall be initialized from its brace-or-equal-initializer or, if there is
no brace-or-equalinitializer, from an empty initializer list (8.5.4).
--
Best regards,
Andrey Tarasevich
[toc] | [prev] | [next] | [standalone]
| From | Juha Nieminen <nospam@thanks.invalid> |
|---|---|
| Date | 2022-02-09 07:26 +0000 |
| Message-ID | <stvqbi$1ulv$1@gioia.aioe.org> |
| In reply to | #82973 |
Andrey Tarasevich <andreytarasevich@hotmail.com> wrote: > Aggregate initialization in C++ never used any "constructors" at the top > level. It is a separate built-in initialization mechanism with its own > custom specification. So, in C++14 this specification was simply > modified to take into account default initializers: > > 8.5.1 Aggregates > 7 If there are fewer initializer-clauses in the list than there are > members in the aggregate, then each member not explicitly initialized > shall be initialized from its brace-or-equal-initializer or, if there is > no brace-or-equalinitializer, from an empty initializer list (8.5.4). I see. It makes sense. (Although I wonder how they didn't think of that when they were standardizing C++11.) Also, I think someone should update the cppreferece.com page to include this information.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c++
csiph-web