Why does aggregate initialization not work anymore since C++20 if a constructor is explicitly defaulted or deleted?How exactly does __attribute__((constructor)) work?why explicitly delete the constructor?Inheriting-Constructors + In-Class-Initialization of non-default constructabe type failsdefault/delete move constructor and assignment with the existence of RVOWhy does a class with a user-declared destructor have an implicitly defaulted constructor?Why C++11 implicitly deletes my default constructor?Why are implicitly and explicitly deleted move constructors treated differently?Unions in C++11: default constructor seems to be deletedDefault move constructor/assignment and deleted copy constructor/assignmentInitialisation of aggregate with default constructor deleted in c++20
In a topological space if there exists a loop that cannot be contracted to a point does there exist a simple loop that cannot be contracted also?
Why aren’t emergency services using callsigns?
(11 of 11: Meta) What is Pyramid Cult's All-Time Favorite?
Infeasibility in mathematical optimization models
Does this Foo machine halt?
Strangeness with gears
What does Apple mean by "This may decrease battery life"?
In reversi, can you overwrite two chips in one move?
Was the 2019 Lion King film made through motion capture?
'sudo apt-get update' get a warning
A stranger from Norway wants to have money delivered to me
Why are Gatwick's runways too close together?
Is it incorrect to write "I rate this book a 3 out of 4 stars?"
Best gun to modify into a monsterhunter weapon?
Visa National - No Exit Stamp From France on Return to the UK
Author changing name
Why is しない used instead of じゃない?
Does a code snippet compile? Or does it get compiled?
As a 16 year old, how can I keep my money safe from my mother?
Is it really ~648.69 km/s delta-v to "land" on the surface of the Sun?
Accidentals - some in brackets, some not
What are good ways to improve as a writer other than writing courses?
Does two puncture wounds mean venomous snake?
During the Space Shuttle Columbia Disaster of 2003, Why Did The Flight Director Say, "Lock the doors."?
Why does aggregate initialization not work anymore since C++20 if a constructor is explicitly defaulted or deleted?
How exactly does __attribute__((constructor)) work?why explicitly delete the constructor?Inheriting-Constructors + In-Class-Initialization of non-default constructabe type failsdefault/delete move constructor and assignment with the existence of RVOWhy does a class with a user-declared destructor have an implicitly defaulted constructor?Why C++11 implicitly deletes my default constructor?Why are implicitly and explicitly deleted move constructors treated differently?Unions in C++11: default constructor seems to be deletedDefault move constructor/assignment and deleted copy constructor/assignmentInitialisation of aggregate with default constructor deleted in c++20
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
I'm migrating a C++ Visual Studio Project from VS2017 to VS2019.
I'm getting an error now, that didn't occur before, that can be reproduced with these few lines of code:
struct Foo
Foo() = default;
int bar;
;
auto test = Foo 0 ;
The error is
(6): error C2440: 'initializing': cannot convert from
'initializer list' to 'Foo'
(6): note: No constructor could take the source type, or
constructor overload resolution was ambiguous
The project is compiled with /std:c++latest
flag. I reproduced it on godbolt. If I switch it to /std:c++17
, it compiles fine as before.
I tried to compile the same code with clang with -std=c++2a
and got a similar error. Also, defaulting or deleting other constructors generates this error.
Apparently, some new C++20 features were added in VS2019 and I'm assuming the origin of this issue is described in https://en.cppreference.com/w/cpp/language/aggregate_initialization.
There it says that an aggregate can be a struct that (among other criteria) has
- no user-provided, inherited, or explicit constructors (explicitly defaulted or deleted constructors are allowed) (since C++17) (until C++20)
- no user-declared or inherited constructors (since C++20)
Note that the part in parentheses "explicitly defaulted or deleted constructors are allowed" was dropped and that "user-provided" changed to "user-declared".
So my first question is, am I right assuming that this change in the standard is the reason why my code compiled before but does not anymore?
Of course, it's easy to fix this: Just remove the explicitly defaulted constructors.
However, I have explicitly defaulted and deleted very many constructors in all of my projects because I found it was a good habit to make code much more expressive this way because it simply results in fewer surprises than with implicitly defaulted or deleted constructors. With this change however, this doesn't seem like such a good habit anymore...
So my actual question is:
What is the reasoning behind this change from C++17 to C++20? Was this break of backwards compatibility made on purpose? Was there some trade off like "Ok, we're breaking backwards compatibility here, but it's for the greater good."? What is this greater good?
c++ c++17 backwards-compatibility c++20
add a comment |
I'm migrating a C++ Visual Studio Project from VS2017 to VS2019.
I'm getting an error now, that didn't occur before, that can be reproduced with these few lines of code:
struct Foo
Foo() = default;
int bar;
;
auto test = Foo 0 ;
The error is
(6): error C2440: 'initializing': cannot convert from
'initializer list' to 'Foo'
(6): note: No constructor could take the source type, or
constructor overload resolution was ambiguous
The project is compiled with /std:c++latest
flag. I reproduced it on godbolt. If I switch it to /std:c++17
, it compiles fine as before.
I tried to compile the same code with clang with -std=c++2a
and got a similar error. Also, defaulting or deleting other constructors generates this error.
Apparently, some new C++20 features were added in VS2019 and I'm assuming the origin of this issue is described in https://en.cppreference.com/w/cpp/language/aggregate_initialization.
There it says that an aggregate can be a struct that (among other criteria) has
- no user-provided, inherited, or explicit constructors (explicitly defaulted or deleted constructors are allowed) (since C++17) (until C++20)
- no user-declared or inherited constructors (since C++20)
Note that the part in parentheses "explicitly defaulted or deleted constructors are allowed" was dropped and that "user-provided" changed to "user-declared".
So my first question is, am I right assuming that this change in the standard is the reason why my code compiled before but does not anymore?
Of course, it's easy to fix this: Just remove the explicitly defaulted constructors.
However, I have explicitly defaulted and deleted very many constructors in all of my projects because I found it was a good habit to make code much more expressive this way because it simply results in fewer surprises than with implicitly defaulted or deleted constructors. With this change however, this doesn't seem like such a good habit anymore...
So my actual question is:
What is the reasoning behind this change from C++17 to C++20? Was this break of backwards compatibility made on purpose? Was there some trade off like "Ok, we're breaking backwards compatibility here, but it's for the greater good."? What is this greater good?
c++ c++17 backwards-compatibility c++20
2
This is the paper. I don't find the rationale (which IMO boils down to "OMG these contrived examples are so surprising they must be fixed") persuasive. YMMV.
– T.C.
Jul 30 at 12:20
I'd still consider it a good habit, I'd rather consider aggregate initialisation a bad habit...
– Aconcagua
Jul 30 at 12:24
1
To answer the less-important first question, explicitly defaulted constructors are considered to be user-declared, but not user-provided. Thus, the change in wording there is indeed the reason for the new errors. (As a note, the standard falters a little in regards to the term "user-declared", by not properly defining it. It's roughly used as a counterpart to "implicitly-declared", though, which (when combined with the term itself) is enough to, ahem, implicitly define it.)
– Justin Time
Jul 30 at 22:13
add a comment |
I'm migrating a C++ Visual Studio Project from VS2017 to VS2019.
I'm getting an error now, that didn't occur before, that can be reproduced with these few lines of code:
struct Foo
Foo() = default;
int bar;
;
auto test = Foo 0 ;
The error is
(6): error C2440: 'initializing': cannot convert from
'initializer list' to 'Foo'
(6): note: No constructor could take the source type, or
constructor overload resolution was ambiguous
The project is compiled with /std:c++latest
flag. I reproduced it on godbolt. If I switch it to /std:c++17
, it compiles fine as before.
I tried to compile the same code with clang with -std=c++2a
and got a similar error. Also, defaulting or deleting other constructors generates this error.
Apparently, some new C++20 features were added in VS2019 and I'm assuming the origin of this issue is described in https://en.cppreference.com/w/cpp/language/aggregate_initialization.
There it says that an aggregate can be a struct that (among other criteria) has
- no user-provided, inherited, or explicit constructors (explicitly defaulted or deleted constructors are allowed) (since C++17) (until C++20)
- no user-declared or inherited constructors (since C++20)
Note that the part in parentheses "explicitly defaulted or deleted constructors are allowed" was dropped and that "user-provided" changed to "user-declared".
So my first question is, am I right assuming that this change in the standard is the reason why my code compiled before but does not anymore?
Of course, it's easy to fix this: Just remove the explicitly defaulted constructors.
However, I have explicitly defaulted and deleted very many constructors in all of my projects because I found it was a good habit to make code much more expressive this way because it simply results in fewer surprises than with implicitly defaulted or deleted constructors. With this change however, this doesn't seem like such a good habit anymore...
So my actual question is:
What is the reasoning behind this change from C++17 to C++20? Was this break of backwards compatibility made on purpose? Was there some trade off like "Ok, we're breaking backwards compatibility here, but it's for the greater good."? What is this greater good?
c++ c++17 backwards-compatibility c++20
I'm migrating a C++ Visual Studio Project from VS2017 to VS2019.
I'm getting an error now, that didn't occur before, that can be reproduced with these few lines of code:
struct Foo
Foo() = default;
int bar;
;
auto test = Foo 0 ;
The error is
(6): error C2440: 'initializing': cannot convert from
'initializer list' to 'Foo'
(6): note: No constructor could take the source type, or
constructor overload resolution was ambiguous
The project is compiled with /std:c++latest
flag. I reproduced it on godbolt. If I switch it to /std:c++17
, it compiles fine as before.
I tried to compile the same code with clang with -std=c++2a
and got a similar error. Also, defaulting or deleting other constructors generates this error.
Apparently, some new C++20 features were added in VS2019 and I'm assuming the origin of this issue is described in https://en.cppreference.com/w/cpp/language/aggregate_initialization.
There it says that an aggregate can be a struct that (among other criteria) has
- no user-provided, inherited, or explicit constructors (explicitly defaulted or deleted constructors are allowed) (since C++17) (until C++20)
- no user-declared or inherited constructors (since C++20)
Note that the part in parentheses "explicitly defaulted or deleted constructors are allowed" was dropped and that "user-provided" changed to "user-declared".
So my first question is, am I right assuming that this change in the standard is the reason why my code compiled before but does not anymore?
Of course, it's easy to fix this: Just remove the explicitly defaulted constructors.
However, I have explicitly defaulted and deleted very many constructors in all of my projects because I found it was a good habit to make code much more expressive this way because it simply results in fewer surprises than with implicitly defaulted or deleted constructors. With this change however, this doesn't seem like such a good habit anymore...
So my actual question is:
What is the reasoning behind this change from C++17 to C++20? Was this break of backwards compatibility made on purpose? Was there some trade off like "Ok, we're breaking backwards compatibility here, but it's for the greater good."? What is this greater good?
c++ c++17 backwards-compatibility c++20
c++ c++17 backwards-compatibility c++20
edited Jul 31 at 7:34
sebrockm
asked Jul 30 at 12:12
sebrockmsebrockm
2,6611 gold badge4 silver badges23 bronze badges
2,6611 gold badge4 silver badges23 bronze badges
2
This is the paper. I don't find the rationale (which IMO boils down to "OMG these contrived examples are so surprising they must be fixed") persuasive. YMMV.
– T.C.
Jul 30 at 12:20
I'd still consider it a good habit, I'd rather consider aggregate initialisation a bad habit...
– Aconcagua
Jul 30 at 12:24
1
To answer the less-important first question, explicitly defaulted constructors are considered to be user-declared, but not user-provided. Thus, the change in wording there is indeed the reason for the new errors. (As a note, the standard falters a little in regards to the term "user-declared", by not properly defining it. It's roughly used as a counterpart to "implicitly-declared", though, which (when combined with the term itself) is enough to, ahem, implicitly define it.)
– Justin Time
Jul 30 at 22:13
add a comment |
2
This is the paper. I don't find the rationale (which IMO boils down to "OMG these contrived examples are so surprising they must be fixed") persuasive. YMMV.
– T.C.
Jul 30 at 12:20
I'd still consider it a good habit, I'd rather consider aggregate initialisation a bad habit...
– Aconcagua
Jul 30 at 12:24
1
To answer the less-important first question, explicitly defaulted constructors are considered to be user-declared, but not user-provided. Thus, the change in wording there is indeed the reason for the new errors. (As a note, the standard falters a little in regards to the term "user-declared", by not properly defining it. It's roughly used as a counterpart to "implicitly-declared", though, which (when combined with the term itself) is enough to, ahem, implicitly define it.)
– Justin Time
Jul 30 at 22:13
2
2
This is the paper. I don't find the rationale (which IMO boils down to "OMG these contrived examples are so surprising they must be fixed") persuasive. YMMV.
– T.C.
Jul 30 at 12:20
This is the paper. I don't find the rationale (which IMO boils down to "OMG these contrived examples are so surprising they must be fixed") persuasive. YMMV.
– T.C.
Jul 30 at 12:20
I'd still consider it a good habit, I'd rather consider aggregate initialisation a bad habit...
– Aconcagua
Jul 30 at 12:24
I'd still consider it a good habit, I'd rather consider aggregate initialisation a bad habit...
– Aconcagua
Jul 30 at 12:24
1
1
To answer the less-important first question, explicitly defaulted constructors are considered to be user-declared, but not user-provided. Thus, the change in wording there is indeed the reason for the new errors. (As a note, the standard falters a little in regards to the term "user-declared", by not properly defining it. It's roughly used as a counterpart to "implicitly-declared", though, which (when combined with the term itself) is enough to, ahem, implicitly define it.)
– Justin Time
Jul 30 at 22:13
To answer the less-important first question, explicitly defaulted constructors are considered to be user-declared, but not user-provided. Thus, the change in wording there is indeed the reason for the new errors. (As a note, the standard falters a little in regards to the term "user-declared", by not properly defining it. It's roughly used as a counterpart to "implicitly-declared", though, which (when combined with the term itself) is enough to, ahem, implicitly define it.)
– Justin Time
Jul 30 at 22:13
add a comment |
3 Answers
3
active
oldest
votes
The abstract from P1008, the proposal that led to the change:
C++ currently allows some types with user-declared constructors to be initialized via aggregate initialization, bypassing those constructors. The result is code that is surprising, confusing, and buggy. This paper proposes a fix that makes initialization semantics in C++ safer, more uniform,and easier to teach. We also discuss the breaking changes that this fix introduces.
One of the examples they give is the following.
struct X
int i4;
X() = default;
;
int main()
X x1(3); // ill-formed - no matching c’tor
X x23; // compiles!
To me, it's quite clear that the proposed changes are worth the backwards-incompatibility they bear. And indeed, it doesn't seem to be good practice anymore to = default
aggregate default constructors.
1
Shouldn't it beX x23
instead – or comma instead of semicolon after first variable? Apart from, I do consider it good practice to default the constructors, I rather consider aggregate initialisation bad practice (which we now can prevent this way).
– Aconcagua
Jul 30 at 12:27
@Aconcagua Thanks for the hint. Copy-pasting from a pdf there...
– lubgr
Jul 30 at 12:28
3
Well, they did just makeX x1(3);
well-formed for aggregates...
– T.C.
Jul 30 at 13:48
@Aconcagua, why do you think that aggregate initialisation is bad practice?
– Patrick Fromberg
Aug 7 at 19:54
@PatrickFromberg For aggregate initialisation, you rely on member order as we do not have designated initialisers (yet, at least) as we have in C. Imagine you need to add a new member, and for alignment or other reasons you need to place it in between some others. That will break aggregate initialisation. Constructors can be fixed appropriately, in contrast, so they are just safer.
– Aconcagua
2 days ago
|
show 1 more comment
The reasoning from P1008 (PDF) can be best understood from two directions:
- If you sat a relatively new C++ programmer down in front of a class definition and ask "is this an aggregate", would they be correct?
The common conception of an aggregate is "a class with no constructors". If Typename() = default;
is in a class definition, most people will see that as having a constructor. It will behave like the standard default constructor, but the type still has one. That is the broad conception of the idea from many users.
An aggregate is supposed to be a class of pure data, able to have any member assume any value it is given. From that perspective, you have no business giving it constructors of any kind, even if you defaulted them. Which brings us to the next reasoning:
- If my class fulfills the requirements of an aggregate, but I don't want it to be an aggregate, how do I do that?
The most obvious answer would be to = default
the default constructor, because I'm probably someone from group #1. Obviously, that doesn't work.
Pre-C++20, your options are to give the class some other constructor or to implement one of the special member functions. Neither of these options are palatable, because (by definition) it's not something you actually need to implement; you're just doing it to make some side effect happen.
Post-C++20, the obvious answer works.
By changing the rules in such a way, it makes the difference between an aggregate and non-aggregate visible. Aggregates have no constructors; so if you want a type to be an aggregate, you don't give it constructors.
Oh, and here's a fun fact: pre-C++20, this is an aggregate:
class Agg
Agg() = default;
;
Note that the defaulted constructor is private, so only people with private access to Agg
can call it... unless they use Agg
, bypasses the constructor and is perfectly legal.
The clear intent of this class is to create a class which can be copied around, but can only get its initial construction from those with private access. This allows forwarding of access controls, as only code which was given an Agg
can call functions that take Agg
as a parameter. And only code with access to Agg
can create one.
Or at least, that's how it is supposed to be.
Now you could fix this more targetedly by saying that it's an aggregate if the defaulted/deleted constructors are not publicly declared. But that feels even more in-congruent; sometimes, a class with a visibly declared constructor is an aggregate and sometimes it isn't, depending on where that visibly declared constructor is.
I see... ok, some convincing arguments here, thanks. Your last example would be even better, if you changed= default
to= delete
as this even more expresses the intent (andAgg
still worked in C++17, uff... was not aware of that).
– sebrockm
Jul 30 at 13:12
@sebrockm: Not really. If you delete the default constructor, then nobody is supposed to be able to create an object of that type (outside of copying). If you make it a private, defaulted constructor, then only people with private access should be able to create one (again, outside of copying).
– Nicol Bolas
Jul 30 at 13:13
right, slightly different intent. Still, "nobody is supposed to create it (but still everyone can)" is an even more convincing argument than "only some chosen ones are supposed to create it (but still everyone can)". That was my point :)
– sebrockm
Jul 30 at 13:16
1
@sebrockm: But that's easily countered by the argument, "If nobody is supposed to be able to create the object... why does the type exist at all?" And while you can point to various metaprogramming tools which use types purely as engines of computation, none of them actively forbid creating instances of such types, nor are they conceptually broken if someone does so. So why is it important to be able to create a type that cannot be instantiated, rather than just a type where instantiation isn't useful?
– Nicol Bolas
Jul 30 at 13:22
I think we are talking about different things here. I'm not proposing the deletion of the default c'tor and thus expressing "this shall never be created" is a reasonable thing to do. Still, if I did that, I'd expect that absolutely never there will be an instance of this class. But contrary, everybody can create it. To me, that behavior is even a little more counter intuitive than your example where you do expect objects get created (just who can do it, is a surprise, but not that it can be done at all).
– sebrockm
Jul 30 at 13:48
|
show 2 more comments
Actually, MSDN addressed your concern in the below document:
Modified specification of aggregate type
In Visual Studio 2019, under /std:c++latest, a class with any user-declared constructor (for example, including a constructor declared = default or = delete) isn't an aggregate. Previously, only user-provided constructors would disqualify a class from being an aggregate. This change puts additional restrictions on how such types can be initialized.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f57271400%2fwhy-does-aggregate-initialization-not-work-anymore-since-c20-if-a-constructor%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
The abstract from P1008, the proposal that led to the change:
C++ currently allows some types with user-declared constructors to be initialized via aggregate initialization, bypassing those constructors. The result is code that is surprising, confusing, and buggy. This paper proposes a fix that makes initialization semantics in C++ safer, more uniform,and easier to teach. We also discuss the breaking changes that this fix introduces.
One of the examples they give is the following.
struct X
int i4;
X() = default;
;
int main()
X x1(3); // ill-formed - no matching c’tor
X x23; // compiles!
To me, it's quite clear that the proposed changes are worth the backwards-incompatibility they bear. And indeed, it doesn't seem to be good practice anymore to = default
aggregate default constructors.
1
Shouldn't it beX x23
instead – or comma instead of semicolon after first variable? Apart from, I do consider it good practice to default the constructors, I rather consider aggregate initialisation bad practice (which we now can prevent this way).
– Aconcagua
Jul 30 at 12:27
@Aconcagua Thanks for the hint. Copy-pasting from a pdf there...
– lubgr
Jul 30 at 12:28
3
Well, they did just makeX x1(3);
well-formed for aggregates...
– T.C.
Jul 30 at 13:48
@Aconcagua, why do you think that aggregate initialisation is bad practice?
– Patrick Fromberg
Aug 7 at 19:54
@PatrickFromberg For aggregate initialisation, you rely on member order as we do not have designated initialisers (yet, at least) as we have in C. Imagine you need to add a new member, and for alignment or other reasons you need to place it in between some others. That will break aggregate initialisation. Constructors can be fixed appropriately, in contrast, so they are just safer.
– Aconcagua
2 days ago
|
show 1 more comment
The abstract from P1008, the proposal that led to the change:
C++ currently allows some types with user-declared constructors to be initialized via aggregate initialization, bypassing those constructors. The result is code that is surprising, confusing, and buggy. This paper proposes a fix that makes initialization semantics in C++ safer, more uniform,and easier to teach. We also discuss the breaking changes that this fix introduces.
One of the examples they give is the following.
struct X
int i4;
X() = default;
;
int main()
X x1(3); // ill-formed - no matching c’tor
X x23; // compiles!
To me, it's quite clear that the proposed changes are worth the backwards-incompatibility they bear. And indeed, it doesn't seem to be good practice anymore to = default
aggregate default constructors.
1
Shouldn't it beX x23
instead – or comma instead of semicolon after first variable? Apart from, I do consider it good practice to default the constructors, I rather consider aggregate initialisation bad practice (which we now can prevent this way).
– Aconcagua
Jul 30 at 12:27
@Aconcagua Thanks for the hint. Copy-pasting from a pdf there...
– lubgr
Jul 30 at 12:28
3
Well, they did just makeX x1(3);
well-formed for aggregates...
– T.C.
Jul 30 at 13:48
@Aconcagua, why do you think that aggregate initialisation is bad practice?
– Patrick Fromberg
Aug 7 at 19:54
@PatrickFromberg For aggregate initialisation, you rely on member order as we do not have designated initialisers (yet, at least) as we have in C. Imagine you need to add a new member, and for alignment or other reasons you need to place it in between some others. That will break aggregate initialisation. Constructors can be fixed appropriately, in contrast, so they are just safer.
– Aconcagua
2 days ago
|
show 1 more comment
The abstract from P1008, the proposal that led to the change:
C++ currently allows some types with user-declared constructors to be initialized via aggregate initialization, bypassing those constructors. The result is code that is surprising, confusing, and buggy. This paper proposes a fix that makes initialization semantics in C++ safer, more uniform,and easier to teach. We also discuss the breaking changes that this fix introduces.
One of the examples they give is the following.
struct X
int i4;
X() = default;
;
int main()
X x1(3); // ill-formed - no matching c’tor
X x23; // compiles!
To me, it's quite clear that the proposed changes are worth the backwards-incompatibility they bear. And indeed, it doesn't seem to be good practice anymore to = default
aggregate default constructors.
The abstract from P1008, the proposal that led to the change:
C++ currently allows some types with user-declared constructors to be initialized via aggregate initialization, bypassing those constructors. The result is code that is surprising, confusing, and buggy. This paper proposes a fix that makes initialization semantics in C++ safer, more uniform,and easier to teach. We also discuss the breaking changes that this fix introduces.
One of the examples they give is the following.
struct X
int i4;
X() = default;
;
int main()
X x1(3); // ill-formed - no matching c’tor
X x23; // compiles!
To me, it's quite clear that the proposed changes are worth the backwards-incompatibility they bear. And indeed, it doesn't seem to be good practice anymore to = default
aggregate default constructors.
edited Jul 30 at 12:28
answered Jul 30 at 12:24
lubgrlubgr
24.3k3 gold badges32 silver badges77 bronze badges
24.3k3 gold badges32 silver badges77 bronze badges
1
Shouldn't it beX x23
instead – or comma instead of semicolon after first variable? Apart from, I do consider it good practice to default the constructors, I rather consider aggregate initialisation bad practice (which we now can prevent this way).
– Aconcagua
Jul 30 at 12:27
@Aconcagua Thanks for the hint. Copy-pasting from a pdf there...
– lubgr
Jul 30 at 12:28
3
Well, they did just makeX x1(3);
well-formed for aggregates...
– T.C.
Jul 30 at 13:48
@Aconcagua, why do you think that aggregate initialisation is bad practice?
– Patrick Fromberg
Aug 7 at 19:54
@PatrickFromberg For aggregate initialisation, you rely on member order as we do not have designated initialisers (yet, at least) as we have in C. Imagine you need to add a new member, and for alignment or other reasons you need to place it in between some others. That will break aggregate initialisation. Constructors can be fixed appropriately, in contrast, so they are just safer.
– Aconcagua
2 days ago
|
show 1 more comment
1
Shouldn't it beX x23
instead – or comma instead of semicolon after first variable? Apart from, I do consider it good practice to default the constructors, I rather consider aggregate initialisation bad practice (which we now can prevent this way).
– Aconcagua
Jul 30 at 12:27
@Aconcagua Thanks for the hint. Copy-pasting from a pdf there...
– lubgr
Jul 30 at 12:28
3
Well, they did just makeX x1(3);
well-formed for aggregates...
– T.C.
Jul 30 at 13:48
@Aconcagua, why do you think that aggregate initialisation is bad practice?
– Patrick Fromberg
Aug 7 at 19:54
@PatrickFromberg For aggregate initialisation, you rely on member order as we do not have designated initialisers (yet, at least) as we have in C. Imagine you need to add a new member, and for alignment or other reasons you need to place it in between some others. That will break aggregate initialisation. Constructors can be fixed appropriately, in contrast, so they are just safer.
– Aconcagua
2 days ago
1
1
Shouldn't it be
X x23
instead – or comma instead of semicolon after first variable? Apart from, I do consider it good practice to default the constructors, I rather consider aggregate initialisation bad practice (which we now can prevent this way).– Aconcagua
Jul 30 at 12:27
Shouldn't it be
X x23
instead – or comma instead of semicolon after first variable? Apart from, I do consider it good practice to default the constructors, I rather consider aggregate initialisation bad practice (which we now can prevent this way).– Aconcagua
Jul 30 at 12:27
@Aconcagua Thanks for the hint. Copy-pasting from a pdf there...
– lubgr
Jul 30 at 12:28
@Aconcagua Thanks for the hint. Copy-pasting from a pdf there...
– lubgr
Jul 30 at 12:28
3
3
Well, they did just make
X x1(3);
well-formed for aggregates...– T.C.
Jul 30 at 13:48
Well, they did just make
X x1(3);
well-formed for aggregates...– T.C.
Jul 30 at 13:48
@Aconcagua, why do you think that aggregate initialisation is bad practice?
– Patrick Fromberg
Aug 7 at 19:54
@Aconcagua, why do you think that aggregate initialisation is bad practice?
– Patrick Fromberg
Aug 7 at 19:54
@PatrickFromberg For aggregate initialisation, you rely on member order as we do not have designated initialisers (yet, at least) as we have in C. Imagine you need to add a new member, and for alignment or other reasons you need to place it in between some others. That will break aggregate initialisation. Constructors can be fixed appropriately, in contrast, so they are just safer.
– Aconcagua
2 days ago
@PatrickFromberg For aggregate initialisation, you rely on member order as we do not have designated initialisers (yet, at least) as we have in C. Imagine you need to add a new member, and for alignment or other reasons you need to place it in between some others. That will break aggregate initialisation. Constructors can be fixed appropriately, in contrast, so they are just safer.
– Aconcagua
2 days ago
|
show 1 more comment
The reasoning from P1008 (PDF) can be best understood from two directions:
- If you sat a relatively new C++ programmer down in front of a class definition and ask "is this an aggregate", would they be correct?
The common conception of an aggregate is "a class with no constructors". If Typename() = default;
is in a class definition, most people will see that as having a constructor. It will behave like the standard default constructor, but the type still has one. That is the broad conception of the idea from many users.
An aggregate is supposed to be a class of pure data, able to have any member assume any value it is given. From that perspective, you have no business giving it constructors of any kind, even if you defaulted them. Which brings us to the next reasoning:
- If my class fulfills the requirements of an aggregate, but I don't want it to be an aggregate, how do I do that?
The most obvious answer would be to = default
the default constructor, because I'm probably someone from group #1. Obviously, that doesn't work.
Pre-C++20, your options are to give the class some other constructor or to implement one of the special member functions. Neither of these options are palatable, because (by definition) it's not something you actually need to implement; you're just doing it to make some side effect happen.
Post-C++20, the obvious answer works.
By changing the rules in such a way, it makes the difference between an aggregate and non-aggregate visible. Aggregates have no constructors; so if you want a type to be an aggregate, you don't give it constructors.
Oh, and here's a fun fact: pre-C++20, this is an aggregate:
class Agg
Agg() = default;
;
Note that the defaulted constructor is private, so only people with private access to Agg
can call it... unless they use Agg
, bypasses the constructor and is perfectly legal.
The clear intent of this class is to create a class which can be copied around, but can only get its initial construction from those with private access. This allows forwarding of access controls, as only code which was given an Agg
can call functions that take Agg
as a parameter. And only code with access to Agg
can create one.
Or at least, that's how it is supposed to be.
Now you could fix this more targetedly by saying that it's an aggregate if the defaulted/deleted constructors are not publicly declared. But that feels even more in-congruent; sometimes, a class with a visibly declared constructor is an aggregate and sometimes it isn't, depending on where that visibly declared constructor is.
I see... ok, some convincing arguments here, thanks. Your last example would be even better, if you changed= default
to= delete
as this even more expresses the intent (andAgg
still worked in C++17, uff... was not aware of that).
– sebrockm
Jul 30 at 13:12
@sebrockm: Not really. If you delete the default constructor, then nobody is supposed to be able to create an object of that type (outside of copying). If you make it a private, defaulted constructor, then only people with private access should be able to create one (again, outside of copying).
– Nicol Bolas
Jul 30 at 13:13
right, slightly different intent. Still, "nobody is supposed to create it (but still everyone can)" is an even more convincing argument than "only some chosen ones are supposed to create it (but still everyone can)". That was my point :)
– sebrockm
Jul 30 at 13:16
1
@sebrockm: But that's easily countered by the argument, "If nobody is supposed to be able to create the object... why does the type exist at all?" And while you can point to various metaprogramming tools which use types purely as engines of computation, none of them actively forbid creating instances of such types, nor are they conceptually broken if someone does so. So why is it important to be able to create a type that cannot be instantiated, rather than just a type where instantiation isn't useful?
– Nicol Bolas
Jul 30 at 13:22
I think we are talking about different things here. I'm not proposing the deletion of the default c'tor and thus expressing "this shall never be created" is a reasonable thing to do. Still, if I did that, I'd expect that absolutely never there will be an instance of this class. But contrary, everybody can create it. To me, that behavior is even a little more counter intuitive than your example where you do expect objects get created (just who can do it, is a surprise, but not that it can be done at all).
– sebrockm
Jul 30 at 13:48
|
show 2 more comments
The reasoning from P1008 (PDF) can be best understood from two directions:
- If you sat a relatively new C++ programmer down in front of a class definition and ask "is this an aggregate", would they be correct?
The common conception of an aggregate is "a class with no constructors". If Typename() = default;
is in a class definition, most people will see that as having a constructor. It will behave like the standard default constructor, but the type still has one. That is the broad conception of the idea from many users.
An aggregate is supposed to be a class of pure data, able to have any member assume any value it is given. From that perspective, you have no business giving it constructors of any kind, even if you defaulted them. Which brings us to the next reasoning:
- If my class fulfills the requirements of an aggregate, but I don't want it to be an aggregate, how do I do that?
The most obvious answer would be to = default
the default constructor, because I'm probably someone from group #1. Obviously, that doesn't work.
Pre-C++20, your options are to give the class some other constructor or to implement one of the special member functions. Neither of these options are palatable, because (by definition) it's not something you actually need to implement; you're just doing it to make some side effect happen.
Post-C++20, the obvious answer works.
By changing the rules in such a way, it makes the difference between an aggregate and non-aggregate visible. Aggregates have no constructors; so if you want a type to be an aggregate, you don't give it constructors.
Oh, and here's a fun fact: pre-C++20, this is an aggregate:
class Agg
Agg() = default;
;
Note that the defaulted constructor is private, so only people with private access to Agg
can call it... unless they use Agg
, bypasses the constructor and is perfectly legal.
The clear intent of this class is to create a class which can be copied around, but can only get its initial construction from those with private access. This allows forwarding of access controls, as only code which was given an Agg
can call functions that take Agg
as a parameter. And only code with access to Agg
can create one.
Or at least, that's how it is supposed to be.
Now you could fix this more targetedly by saying that it's an aggregate if the defaulted/deleted constructors are not publicly declared. But that feels even more in-congruent; sometimes, a class with a visibly declared constructor is an aggregate and sometimes it isn't, depending on where that visibly declared constructor is.
I see... ok, some convincing arguments here, thanks. Your last example would be even better, if you changed= default
to= delete
as this even more expresses the intent (andAgg
still worked in C++17, uff... was not aware of that).
– sebrockm
Jul 30 at 13:12
@sebrockm: Not really. If you delete the default constructor, then nobody is supposed to be able to create an object of that type (outside of copying). If you make it a private, defaulted constructor, then only people with private access should be able to create one (again, outside of copying).
– Nicol Bolas
Jul 30 at 13:13
right, slightly different intent. Still, "nobody is supposed to create it (but still everyone can)" is an even more convincing argument than "only some chosen ones are supposed to create it (but still everyone can)". That was my point :)
– sebrockm
Jul 30 at 13:16
1
@sebrockm: But that's easily countered by the argument, "If nobody is supposed to be able to create the object... why does the type exist at all?" And while you can point to various metaprogramming tools which use types purely as engines of computation, none of them actively forbid creating instances of such types, nor are they conceptually broken if someone does so. So why is it important to be able to create a type that cannot be instantiated, rather than just a type where instantiation isn't useful?
– Nicol Bolas
Jul 30 at 13:22
I think we are talking about different things here. I'm not proposing the deletion of the default c'tor and thus expressing "this shall never be created" is a reasonable thing to do. Still, if I did that, I'd expect that absolutely never there will be an instance of this class. But contrary, everybody can create it. To me, that behavior is even a little more counter intuitive than your example where you do expect objects get created (just who can do it, is a surprise, but not that it can be done at all).
– sebrockm
Jul 30 at 13:48
|
show 2 more comments
The reasoning from P1008 (PDF) can be best understood from two directions:
- If you sat a relatively new C++ programmer down in front of a class definition and ask "is this an aggregate", would they be correct?
The common conception of an aggregate is "a class with no constructors". If Typename() = default;
is in a class definition, most people will see that as having a constructor. It will behave like the standard default constructor, but the type still has one. That is the broad conception of the idea from many users.
An aggregate is supposed to be a class of pure data, able to have any member assume any value it is given. From that perspective, you have no business giving it constructors of any kind, even if you defaulted them. Which brings us to the next reasoning:
- If my class fulfills the requirements of an aggregate, but I don't want it to be an aggregate, how do I do that?
The most obvious answer would be to = default
the default constructor, because I'm probably someone from group #1. Obviously, that doesn't work.
Pre-C++20, your options are to give the class some other constructor or to implement one of the special member functions. Neither of these options are palatable, because (by definition) it's not something you actually need to implement; you're just doing it to make some side effect happen.
Post-C++20, the obvious answer works.
By changing the rules in such a way, it makes the difference between an aggregate and non-aggregate visible. Aggregates have no constructors; so if you want a type to be an aggregate, you don't give it constructors.
Oh, and here's a fun fact: pre-C++20, this is an aggregate:
class Agg
Agg() = default;
;
Note that the defaulted constructor is private, so only people with private access to Agg
can call it... unless they use Agg
, bypasses the constructor and is perfectly legal.
The clear intent of this class is to create a class which can be copied around, but can only get its initial construction from those with private access. This allows forwarding of access controls, as only code which was given an Agg
can call functions that take Agg
as a parameter. And only code with access to Agg
can create one.
Or at least, that's how it is supposed to be.
Now you could fix this more targetedly by saying that it's an aggregate if the defaulted/deleted constructors are not publicly declared. But that feels even more in-congruent; sometimes, a class with a visibly declared constructor is an aggregate and sometimes it isn't, depending on where that visibly declared constructor is.
The reasoning from P1008 (PDF) can be best understood from two directions:
- If you sat a relatively new C++ programmer down in front of a class definition and ask "is this an aggregate", would they be correct?
The common conception of an aggregate is "a class with no constructors". If Typename() = default;
is in a class definition, most people will see that as having a constructor. It will behave like the standard default constructor, but the type still has one. That is the broad conception of the idea from many users.
An aggregate is supposed to be a class of pure data, able to have any member assume any value it is given. From that perspective, you have no business giving it constructors of any kind, even if you defaulted them. Which brings us to the next reasoning:
- If my class fulfills the requirements of an aggregate, but I don't want it to be an aggregate, how do I do that?
The most obvious answer would be to = default
the default constructor, because I'm probably someone from group #1. Obviously, that doesn't work.
Pre-C++20, your options are to give the class some other constructor or to implement one of the special member functions. Neither of these options are palatable, because (by definition) it's not something you actually need to implement; you're just doing it to make some side effect happen.
Post-C++20, the obvious answer works.
By changing the rules in such a way, it makes the difference between an aggregate and non-aggregate visible. Aggregates have no constructors; so if you want a type to be an aggregate, you don't give it constructors.
Oh, and here's a fun fact: pre-C++20, this is an aggregate:
class Agg
Agg() = default;
;
Note that the defaulted constructor is private, so only people with private access to Agg
can call it... unless they use Agg
, bypasses the constructor and is perfectly legal.
The clear intent of this class is to create a class which can be copied around, but can only get its initial construction from those with private access. This allows forwarding of access controls, as only code which was given an Agg
can call functions that take Agg
as a parameter. And only code with access to Agg
can create one.
Or at least, that's how it is supposed to be.
Now you could fix this more targetedly by saying that it's an aggregate if the defaulted/deleted constructors are not publicly declared. But that feels even more in-congruent; sometimes, a class with a visibly declared constructor is an aggregate and sometimes it isn't, depending on where that visibly declared constructor is.
answered Jul 30 at 13:00
Nicol BolasNicol Bolas
304k37 gold badges509 silver badges689 bronze badges
304k37 gold badges509 silver badges689 bronze badges
I see... ok, some convincing arguments here, thanks. Your last example would be even better, if you changed= default
to= delete
as this even more expresses the intent (andAgg
still worked in C++17, uff... was not aware of that).
– sebrockm
Jul 30 at 13:12
@sebrockm: Not really. If you delete the default constructor, then nobody is supposed to be able to create an object of that type (outside of copying). If you make it a private, defaulted constructor, then only people with private access should be able to create one (again, outside of copying).
– Nicol Bolas
Jul 30 at 13:13
right, slightly different intent. Still, "nobody is supposed to create it (but still everyone can)" is an even more convincing argument than "only some chosen ones are supposed to create it (but still everyone can)". That was my point :)
– sebrockm
Jul 30 at 13:16
1
@sebrockm: But that's easily countered by the argument, "If nobody is supposed to be able to create the object... why does the type exist at all?" And while you can point to various metaprogramming tools which use types purely as engines of computation, none of them actively forbid creating instances of such types, nor are they conceptually broken if someone does so. So why is it important to be able to create a type that cannot be instantiated, rather than just a type where instantiation isn't useful?
– Nicol Bolas
Jul 30 at 13:22
I think we are talking about different things here. I'm not proposing the deletion of the default c'tor and thus expressing "this shall never be created" is a reasonable thing to do. Still, if I did that, I'd expect that absolutely never there will be an instance of this class. But contrary, everybody can create it. To me, that behavior is even a little more counter intuitive than your example where you do expect objects get created (just who can do it, is a surprise, but not that it can be done at all).
– sebrockm
Jul 30 at 13:48
|
show 2 more comments
I see... ok, some convincing arguments here, thanks. Your last example would be even better, if you changed= default
to= delete
as this even more expresses the intent (andAgg
still worked in C++17, uff... was not aware of that).
– sebrockm
Jul 30 at 13:12
@sebrockm: Not really. If you delete the default constructor, then nobody is supposed to be able to create an object of that type (outside of copying). If you make it a private, defaulted constructor, then only people with private access should be able to create one (again, outside of copying).
– Nicol Bolas
Jul 30 at 13:13
right, slightly different intent. Still, "nobody is supposed to create it (but still everyone can)" is an even more convincing argument than "only some chosen ones are supposed to create it (but still everyone can)". That was my point :)
– sebrockm
Jul 30 at 13:16
1
@sebrockm: But that's easily countered by the argument, "If nobody is supposed to be able to create the object... why does the type exist at all?" And while you can point to various metaprogramming tools which use types purely as engines of computation, none of them actively forbid creating instances of such types, nor are they conceptually broken if someone does so. So why is it important to be able to create a type that cannot be instantiated, rather than just a type where instantiation isn't useful?
– Nicol Bolas
Jul 30 at 13:22
I think we are talking about different things here. I'm not proposing the deletion of the default c'tor and thus expressing "this shall never be created" is a reasonable thing to do. Still, if I did that, I'd expect that absolutely never there will be an instance of this class. But contrary, everybody can create it. To me, that behavior is even a little more counter intuitive than your example where you do expect objects get created (just who can do it, is a surprise, but not that it can be done at all).
– sebrockm
Jul 30 at 13:48
I see... ok, some convincing arguments here, thanks. Your last example would be even better, if you changed
= default
to = delete
as this even more expresses the intent (and Agg
still worked in C++17, uff... was not aware of that).– sebrockm
Jul 30 at 13:12
I see... ok, some convincing arguments here, thanks. Your last example would be even better, if you changed
= default
to = delete
as this even more expresses the intent (and Agg
still worked in C++17, uff... was not aware of that).– sebrockm
Jul 30 at 13:12
@sebrockm: Not really. If you delete the default constructor, then nobody is supposed to be able to create an object of that type (outside of copying). If you make it a private, defaulted constructor, then only people with private access should be able to create one (again, outside of copying).
– Nicol Bolas
Jul 30 at 13:13
@sebrockm: Not really. If you delete the default constructor, then nobody is supposed to be able to create an object of that type (outside of copying). If you make it a private, defaulted constructor, then only people with private access should be able to create one (again, outside of copying).
– Nicol Bolas
Jul 30 at 13:13
right, slightly different intent. Still, "nobody is supposed to create it (but still everyone can)" is an even more convincing argument than "only some chosen ones are supposed to create it (but still everyone can)". That was my point :)
– sebrockm
Jul 30 at 13:16
right, slightly different intent. Still, "nobody is supposed to create it (but still everyone can)" is an even more convincing argument than "only some chosen ones are supposed to create it (but still everyone can)". That was my point :)
– sebrockm
Jul 30 at 13:16
1
1
@sebrockm: But that's easily countered by the argument, "If nobody is supposed to be able to create the object... why does the type exist at all?" And while you can point to various metaprogramming tools which use types purely as engines of computation, none of them actively forbid creating instances of such types, nor are they conceptually broken if someone does so. So why is it important to be able to create a type that cannot be instantiated, rather than just a type where instantiation isn't useful?
– Nicol Bolas
Jul 30 at 13:22
@sebrockm: But that's easily countered by the argument, "If nobody is supposed to be able to create the object... why does the type exist at all?" And while you can point to various metaprogramming tools which use types purely as engines of computation, none of them actively forbid creating instances of such types, nor are they conceptually broken if someone does so. So why is it important to be able to create a type that cannot be instantiated, rather than just a type where instantiation isn't useful?
– Nicol Bolas
Jul 30 at 13:22
I think we are talking about different things here. I'm not proposing the deletion of the default c'tor and thus expressing "this shall never be created" is a reasonable thing to do. Still, if I did that, I'd expect that absolutely never there will be an instance of this class. But contrary, everybody can create it. To me, that behavior is even a little more counter intuitive than your example where you do expect objects get created (just who can do it, is a surprise, but not that it can be done at all).
– sebrockm
Jul 30 at 13:48
I think we are talking about different things here. I'm not proposing the deletion of the default c'tor and thus expressing "this shall never be created" is a reasonable thing to do. Still, if I did that, I'd expect that absolutely never there will be an instance of this class. But contrary, everybody can create it. To me, that behavior is even a little more counter intuitive than your example where you do expect objects get created (just who can do it, is a surprise, but not that it can be done at all).
– sebrockm
Jul 30 at 13:48
|
show 2 more comments
Actually, MSDN addressed your concern in the below document:
Modified specification of aggregate type
In Visual Studio 2019, under /std:c++latest, a class with any user-declared constructor (for example, including a constructor declared = default or = delete) isn't an aggregate. Previously, only user-provided constructors would disqualify a class from being an aggregate. This change puts additional restrictions on how such types can be initialized.
add a comment |
Actually, MSDN addressed your concern in the below document:
Modified specification of aggregate type
In Visual Studio 2019, under /std:c++latest, a class with any user-declared constructor (for example, including a constructor declared = default or = delete) isn't an aggregate. Previously, only user-provided constructors would disqualify a class from being an aggregate. This change puts additional restrictions on how such types can be initialized.
add a comment |
Actually, MSDN addressed your concern in the below document:
Modified specification of aggregate type
In Visual Studio 2019, under /std:c++latest, a class with any user-declared constructor (for example, including a constructor declared = default or = delete) isn't an aggregate. Previously, only user-provided constructors would disqualify a class from being an aggregate. This change puts additional restrictions on how such types can be initialized.
Actually, MSDN addressed your concern in the below document:
Modified specification of aggregate type
In Visual Studio 2019, under /std:c++latest, a class with any user-declared constructor (for example, including a constructor declared = default or = delete) isn't an aggregate. Previously, only user-provided constructors would disqualify a class from being an aggregate. This change puts additional restrictions on how such types can be initialized.
answered Jul 30 at 12:24
santosh dhanawadesantosh dhanawade
1,21011 silver badges28 bronze badges
1,21011 silver badges28 bronze badges
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f57271400%2fwhy-does-aggregate-initialization-not-work-anymore-since-c20-if-a-constructor%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
This is the paper. I don't find the rationale (which IMO boils down to "OMG these contrived examples are so surprising they must be fixed") persuasive. YMMV.
– T.C.
Jul 30 at 12:20
I'd still consider it a good habit, I'd rather consider aggregate initialisation a bad habit...
– Aconcagua
Jul 30 at 12:24
1
To answer the less-important first question, explicitly defaulted constructors are considered to be user-declared, but not user-provided. Thus, the change in wording there is indeed the reason for the new errors. (As a note, the standard falters a little in regards to the term "user-declared", by not properly defining it. It's roughly used as a counterpart to "implicitly-declared", though, which (when combined with the term itself) is enough to, ahem, implicitly define it.)
– Justin Time
Jul 30 at 22:13