Compilation error when using empty list initialization constructor in C++17
I ran into a weird problem when trying to move to C++17. The problem is that something (and I'm not sure what) changed in C++17 that made list-initialization work differently in the case of a default constructor. I tried to search https://en.cppreference.com/w/cpp/language/list_initialization for more info, but I didn't find anything that looks relevant.
Does someone know the reason the code below compiles in C++14 but not in C++17 when calling B{}
instead of B()
?
(I tried it in both gcc 8.2 and 7.3 and icc 19)
struct A{
protected:
A() {}
};
struct B : public A {};
B f(){
return B(); //compilation OK
//return B{}; //compilation error
}
c++ c++17 list-initialization
|
show 17 more comments
I ran into a weird problem when trying to move to C++17. The problem is that something (and I'm not sure what) changed in C++17 that made list-initialization work differently in the case of a default constructor. I tried to search https://en.cppreference.com/w/cpp/language/list_initialization for more info, but I didn't find anything that looks relevant.
Does someone know the reason the code below compiles in C++14 but not in C++17 when calling B{}
instead of B()
?
(I tried it in both gcc 8.2 and 7.3 and icc 19)
struct A{
protected:
A() {}
};
struct B : public A {};
B f(){
return B(); //compilation OK
//return B{}; //compilation error
}
c++ c++17 list-initialization
1
Try adding this flag:-std=c++0x
. I am not sure though.
– DimChtz
Jan 8 at 13:26
1
@DimChtz That changes the standard to C++11 draft, on compilers old enough to support its pre-standardisation name. Probably not helpful.
– Lightness Races in Orbit
Jan 8 at 13:27
1
@SPlatten Don't worry, you're not alone - almost forty years later many professional C++ developers still don't realise that things defined with the keywordstruct
can have constructors, member functions, access control, bases, ... (and, more reasonably, many of us still try to avoid it for purely aesthetic reasons). It must be the language's best-kept secret, but it has been so for a very, very long time. Anyway, going back to your initial comment, point is, there's nothing mystical or strange or invalid about the OP's typeA
here.
– Lightness Races in Orbit
Jan 8 at 14:24
1
@SPlatten Well, C++ isn't an OO language, so that's reasonable :)
– Lightness Races in Orbit
Jan 8 at 14:26
3
@SPlatten Bjarne may originally have called it "C With Classes", and implied that this makes it OO, but he was wrong. Okay, I'll be charitable and change that to say that the generally-accepted terminology has shifted in the interim, as things were added and times changed. C++ is multi-paradigm, supporting class-based OO as an option but alternatives too (imperative, concurrent, template metaprogramming). Java has elements of some of these things but is fundamentally OO in a way that C++ never was.
– Lightness Races in Orbit
Jan 8 at 14:29
|
show 17 more comments
I ran into a weird problem when trying to move to C++17. The problem is that something (and I'm not sure what) changed in C++17 that made list-initialization work differently in the case of a default constructor. I tried to search https://en.cppreference.com/w/cpp/language/list_initialization for more info, but I didn't find anything that looks relevant.
Does someone know the reason the code below compiles in C++14 but not in C++17 when calling B{}
instead of B()
?
(I tried it in both gcc 8.2 and 7.3 and icc 19)
struct A{
protected:
A() {}
};
struct B : public A {};
B f(){
return B(); //compilation OK
//return B{}; //compilation error
}
c++ c++17 list-initialization
I ran into a weird problem when trying to move to C++17. The problem is that something (and I'm not sure what) changed in C++17 that made list-initialization work differently in the case of a default constructor. I tried to search https://en.cppreference.com/w/cpp/language/list_initialization for more info, but I didn't find anything that looks relevant.
Does someone know the reason the code below compiles in C++14 but not in C++17 when calling B{}
instead of B()
?
(I tried it in both gcc 8.2 and 7.3 and icc 19)
struct A{
protected:
A() {}
};
struct B : public A {};
B f(){
return B(); //compilation OK
//return B{}; //compilation error
}
c++ c++17 list-initialization
c++ c++17 list-initialization
edited Jan 8 at 16:33
TylerH
16.1k105569
16.1k105569
asked Jan 8 at 13:23
RyAraziRyArazi
1117
1117
1
Try adding this flag:-std=c++0x
. I am not sure though.
– DimChtz
Jan 8 at 13:26
1
@DimChtz That changes the standard to C++11 draft, on compilers old enough to support its pre-standardisation name. Probably not helpful.
– Lightness Races in Orbit
Jan 8 at 13:27
1
@SPlatten Don't worry, you're not alone - almost forty years later many professional C++ developers still don't realise that things defined with the keywordstruct
can have constructors, member functions, access control, bases, ... (and, more reasonably, many of us still try to avoid it for purely aesthetic reasons). It must be the language's best-kept secret, but it has been so for a very, very long time. Anyway, going back to your initial comment, point is, there's nothing mystical or strange or invalid about the OP's typeA
here.
– Lightness Races in Orbit
Jan 8 at 14:24
1
@SPlatten Well, C++ isn't an OO language, so that's reasonable :)
– Lightness Races in Orbit
Jan 8 at 14:26
3
@SPlatten Bjarne may originally have called it "C With Classes", and implied that this makes it OO, but he was wrong. Okay, I'll be charitable and change that to say that the generally-accepted terminology has shifted in the interim, as things were added and times changed. C++ is multi-paradigm, supporting class-based OO as an option but alternatives too (imperative, concurrent, template metaprogramming). Java has elements of some of these things but is fundamentally OO in a way that C++ never was.
– Lightness Races in Orbit
Jan 8 at 14:29
|
show 17 more comments
1
Try adding this flag:-std=c++0x
. I am not sure though.
– DimChtz
Jan 8 at 13:26
1
@DimChtz That changes the standard to C++11 draft, on compilers old enough to support its pre-standardisation name. Probably not helpful.
– Lightness Races in Orbit
Jan 8 at 13:27
1
@SPlatten Don't worry, you're not alone - almost forty years later many professional C++ developers still don't realise that things defined with the keywordstruct
can have constructors, member functions, access control, bases, ... (and, more reasonably, many of us still try to avoid it for purely aesthetic reasons). It must be the language's best-kept secret, but it has been so for a very, very long time. Anyway, going back to your initial comment, point is, there's nothing mystical or strange or invalid about the OP's typeA
here.
– Lightness Races in Orbit
Jan 8 at 14:24
1
@SPlatten Well, C++ isn't an OO language, so that's reasonable :)
– Lightness Races in Orbit
Jan 8 at 14:26
3
@SPlatten Bjarne may originally have called it "C With Classes", and implied that this makes it OO, but he was wrong. Okay, I'll be charitable and change that to say that the generally-accepted terminology has shifted in the interim, as things were added and times changed. C++ is multi-paradigm, supporting class-based OO as an option but alternatives too (imperative, concurrent, template metaprogramming). Java has elements of some of these things but is fundamentally OO in a way that C++ never was.
– Lightness Races in Orbit
Jan 8 at 14:29
1
1
Try adding this flag:
-std=c++0x
. I am not sure though.– DimChtz
Jan 8 at 13:26
Try adding this flag:
-std=c++0x
. I am not sure though.– DimChtz
Jan 8 at 13:26
1
1
@DimChtz That changes the standard to C++11 draft, on compilers old enough to support its pre-standardisation name. Probably not helpful.
– Lightness Races in Orbit
Jan 8 at 13:27
@DimChtz That changes the standard to C++11 draft, on compilers old enough to support its pre-standardisation name. Probably not helpful.
– Lightness Races in Orbit
Jan 8 at 13:27
1
1
@SPlatten Don't worry, you're not alone - almost forty years later many professional C++ developers still don't realise that things defined with the keyword
struct
can have constructors, member functions, access control, bases, ... (and, more reasonably, many of us still try to avoid it for purely aesthetic reasons). It must be the language's best-kept secret, but it has been so for a very, very long time. Anyway, going back to your initial comment, point is, there's nothing mystical or strange or invalid about the OP's type A
here.– Lightness Races in Orbit
Jan 8 at 14:24
@SPlatten Don't worry, you're not alone - almost forty years later many professional C++ developers still don't realise that things defined with the keyword
struct
can have constructors, member functions, access control, bases, ... (and, more reasonably, many of us still try to avoid it for purely aesthetic reasons). It must be the language's best-kept secret, but it has been so for a very, very long time. Anyway, going back to your initial comment, point is, there's nothing mystical or strange or invalid about the OP's type A
here.– Lightness Races in Orbit
Jan 8 at 14:24
1
1
@SPlatten Well, C++ isn't an OO language, so that's reasonable :)
– Lightness Races in Orbit
Jan 8 at 14:26
@SPlatten Well, C++ isn't an OO language, so that's reasonable :)
– Lightness Races in Orbit
Jan 8 at 14:26
3
3
@SPlatten Bjarne may originally have called it "C With Classes", and implied that this makes it OO, but he was wrong. Okay, I'll be charitable and change that to say that the generally-accepted terminology has shifted in the interim, as things were added and times changed. C++ is multi-paradigm, supporting class-based OO as an option but alternatives too (imperative, concurrent, template metaprogramming). Java has elements of some of these things but is fundamentally OO in a way that C++ never was.
– Lightness Races in Orbit
Jan 8 at 14:29
@SPlatten Bjarne may originally have called it "C With Classes", and implied that this makes it OO, but he was wrong. Okay, I'll be charitable and change that to say that the generally-accepted terminology has shifted in the interim, as things were added and times changed. C++ is multi-paradigm, supporting class-based OO as an option but alternatives too (imperative, concurrent, template metaprogramming). Java has elements of some of these things but is fundamentally OO in a way that C++ never was.
– Lightness Races in Orbit
Jan 8 at 14:29
|
show 17 more comments
3 Answers
3
active
oldest
votes
In C++14, the definition of aggregate was:
An aggregate is an array or a class (Clause [class]) with no user-provided constructors ([class.ctor]), no private or protected non-static data members (Clause [class.access]), no base classes (Clause [class.derived]), and no virtual functions ([class.virtual]).
Hence, B
is not an aggregate. As a result B{}
is surely not aggregate initialization, and B{}
and B()
end up meaning the same thing. They both just invoke B
's default constructor.
However, in C++17, the definition of aggregate was changed to:
An aggregate is an array or a class with
- no user-provided, explicit, or inherited constructors ([class.ctor]),
- no private or protected non-static data members (Clause [class.access]),
- no virtual functions, and
no virtual, private, or protected base classes ([class.mi]).
[ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors. — end note ]
The restriction is no longer on any base classes, but just on virtual/private/protected ones. But B
has a public base class. It is now an aggregate! And C++17 aggregate initialization does allow for initializing base class subobjects.
In particular, B{}
is aggregate initialization where we just don't provide an initializer for any subobject. But the first (and only) subobject is an A
, which we're trying to initialize from {}
(during aggregate initialization, any subobject without an explicit initializer is copy-initialized from {}
), which we can't do because A
's constructor is protected and we are not a friend (see also, the quoted note).
Note that, just for fun, in C++20 the definition of aggregate will change again.
1
@LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class withB{A{}}
, and if such initializer is missing, an "empty initializer" is added for theA
subobject, and not one for each members ofA
, as I would have thought before.
– Holt
Jan 8 at 14:16
@Holt Yep agreed
– Lightness Races in Orbit
Jan 8 at 14:31
add a comment |
From my understanding of https://en.cppreference.com/w/cpp/language/value_initialization
B{}
does an aggregate_initialization,
and since C++17:
The effects of aggregate initialization are:
- Each direct public base, (since C++17) [..] is copy-initialized from the corresponding clause of the initializer list.
and in our case:
If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.
So B{/*constructor of A*/}
need to construct base class A, which is protected...
add a comment |
The final draft of C++17 n4659 has a compatibility section which contains the changes with respect to previous versions.
C.4.4 Clause 11: declarators [diff.cpp14.decl]
11.6.1
Change: Definition of an aggregate is extended to apply to user-defined types with base classes.
Rationale: To increase
convenience of aggregate initialization.
Effect on original feature:
Valid C++ 2014 code may fail to compile or produce different results
in this International Standard; initialization from an empty
initializer list will perform aggregate initialization instead of
invoking a default constructor for the affected types:
struct derived;
struct base {
friend struct derived;
private:
base();
};
struct derived : base {};
derived d1{}; // Error. The code was well-formed before.
derived d2; // still OK
I compiled the above example code with -std=c++14
and it compiled but failed to compile with -std=c++17
.
I believe that could be the reason why the code in the OP fails with B{}
but succeeds with B()
.
add a comment |
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%2f54092781%2fcompilation-error-when-using-empty-list-initialization-constructor-in-c17%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
In C++14, the definition of aggregate was:
An aggregate is an array or a class (Clause [class]) with no user-provided constructors ([class.ctor]), no private or protected non-static data members (Clause [class.access]), no base classes (Clause [class.derived]), and no virtual functions ([class.virtual]).
Hence, B
is not an aggregate. As a result B{}
is surely not aggregate initialization, and B{}
and B()
end up meaning the same thing. They both just invoke B
's default constructor.
However, in C++17, the definition of aggregate was changed to:
An aggregate is an array or a class with
- no user-provided, explicit, or inherited constructors ([class.ctor]),
- no private or protected non-static data members (Clause [class.access]),
- no virtual functions, and
no virtual, private, or protected base classes ([class.mi]).
[ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors. — end note ]
The restriction is no longer on any base classes, but just on virtual/private/protected ones. But B
has a public base class. It is now an aggregate! And C++17 aggregate initialization does allow for initializing base class subobjects.
In particular, B{}
is aggregate initialization where we just don't provide an initializer for any subobject. But the first (and only) subobject is an A
, which we're trying to initialize from {}
(during aggregate initialization, any subobject without an explicit initializer is copy-initialized from {}
), which we can't do because A
's constructor is protected and we are not a friend (see also, the quoted note).
Note that, just for fun, in C++20 the definition of aggregate will change again.
1
@LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class withB{A{}}
, and if such initializer is missing, an "empty initializer" is added for theA
subobject, and not one for each members ofA
, as I would have thought before.
– Holt
Jan 8 at 14:16
@Holt Yep agreed
– Lightness Races in Orbit
Jan 8 at 14:31
add a comment |
In C++14, the definition of aggregate was:
An aggregate is an array or a class (Clause [class]) with no user-provided constructors ([class.ctor]), no private or protected non-static data members (Clause [class.access]), no base classes (Clause [class.derived]), and no virtual functions ([class.virtual]).
Hence, B
is not an aggregate. As a result B{}
is surely not aggregate initialization, and B{}
and B()
end up meaning the same thing. They both just invoke B
's default constructor.
However, in C++17, the definition of aggregate was changed to:
An aggregate is an array or a class with
- no user-provided, explicit, or inherited constructors ([class.ctor]),
- no private or protected non-static data members (Clause [class.access]),
- no virtual functions, and
no virtual, private, or protected base classes ([class.mi]).
[ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors. — end note ]
The restriction is no longer on any base classes, but just on virtual/private/protected ones. But B
has a public base class. It is now an aggregate! And C++17 aggregate initialization does allow for initializing base class subobjects.
In particular, B{}
is aggregate initialization where we just don't provide an initializer for any subobject. But the first (and only) subobject is an A
, which we're trying to initialize from {}
(during aggregate initialization, any subobject without an explicit initializer is copy-initialized from {}
), which we can't do because A
's constructor is protected and we are not a friend (see also, the quoted note).
Note that, just for fun, in C++20 the definition of aggregate will change again.
1
@LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class withB{A{}}
, and if such initializer is missing, an "empty initializer" is added for theA
subobject, and not one for each members ofA
, as I would have thought before.
– Holt
Jan 8 at 14:16
@Holt Yep agreed
– Lightness Races in Orbit
Jan 8 at 14:31
add a comment |
In C++14, the definition of aggregate was:
An aggregate is an array or a class (Clause [class]) with no user-provided constructors ([class.ctor]), no private or protected non-static data members (Clause [class.access]), no base classes (Clause [class.derived]), and no virtual functions ([class.virtual]).
Hence, B
is not an aggregate. As a result B{}
is surely not aggregate initialization, and B{}
and B()
end up meaning the same thing. They both just invoke B
's default constructor.
However, in C++17, the definition of aggregate was changed to:
An aggregate is an array or a class with
- no user-provided, explicit, or inherited constructors ([class.ctor]),
- no private or protected non-static data members (Clause [class.access]),
- no virtual functions, and
no virtual, private, or protected base classes ([class.mi]).
[ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors. — end note ]
The restriction is no longer on any base classes, but just on virtual/private/protected ones. But B
has a public base class. It is now an aggregate! And C++17 aggregate initialization does allow for initializing base class subobjects.
In particular, B{}
is aggregate initialization where we just don't provide an initializer for any subobject. But the first (and only) subobject is an A
, which we're trying to initialize from {}
(during aggregate initialization, any subobject without an explicit initializer is copy-initialized from {}
), which we can't do because A
's constructor is protected and we are not a friend (see also, the quoted note).
Note that, just for fun, in C++20 the definition of aggregate will change again.
In C++14, the definition of aggregate was:
An aggregate is an array or a class (Clause [class]) with no user-provided constructors ([class.ctor]), no private or protected non-static data members (Clause [class.access]), no base classes (Clause [class.derived]), and no virtual functions ([class.virtual]).
Hence, B
is not an aggregate. As a result B{}
is surely not aggregate initialization, and B{}
and B()
end up meaning the same thing. They both just invoke B
's default constructor.
However, in C++17, the definition of aggregate was changed to:
An aggregate is an array or a class with
- no user-provided, explicit, or inherited constructors ([class.ctor]),
- no private or protected non-static data members (Clause [class.access]),
- no virtual functions, and
no virtual, private, or protected base classes ([class.mi]).
[ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors. — end note ]
The restriction is no longer on any base classes, but just on virtual/private/protected ones. But B
has a public base class. It is now an aggregate! And C++17 aggregate initialization does allow for initializing base class subobjects.
In particular, B{}
is aggregate initialization where we just don't provide an initializer for any subobject. But the first (and only) subobject is an A
, which we're trying to initialize from {}
(during aggregate initialization, any subobject without an explicit initializer is copy-initialized from {}
), which we can't do because A
's constructor is protected and we are not a friend (see also, the quoted note).
Note that, just for fun, in C++20 the definition of aggregate will change again.
answered Jan 8 at 14:01
BarryBarry
186k21327602
186k21327602
1
@LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class withB{A{}}
, and if such initializer is missing, an "empty initializer" is added for theA
subobject, and not one for each members ofA
, as I would have thought before.
– Holt
Jan 8 at 14:16
@Holt Yep agreed
– Lightness Races in Orbit
Jan 8 at 14:31
add a comment |
1
@LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class withB{A{}}
, and if such initializer is missing, an "empty initializer" is added for theA
subobject, and not one for each members ofA
, as I would have thought before.
– Holt
Jan 8 at 14:16
@Holt Yep agreed
– Lightness Races in Orbit
Jan 8 at 14:31
1
1
@LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class with
B{A{}}
, and if such initializer is missing, an "empty initializer" is added for the A
subobject, and not one for each members of A
, as I would have thought before.– Holt
Jan 8 at 14:16
@LightnessRacesinOrbit What was actually confusing for most of us (I think) was how you (can) initialize aggregate with base class with
B{A{}}
, and if such initializer is missing, an "empty initializer" is added for the A
subobject, and not one for each members of A
, as I would have thought before.– Holt
Jan 8 at 14:16
@Holt Yep agreed
– Lightness Races in Orbit
Jan 8 at 14:31
@Holt Yep agreed
– Lightness Races in Orbit
Jan 8 at 14:31
add a comment |
From my understanding of https://en.cppreference.com/w/cpp/language/value_initialization
B{}
does an aggregate_initialization,
and since C++17:
The effects of aggregate initialization are:
- Each direct public base, (since C++17) [..] is copy-initialized from the corresponding clause of the initializer list.
and in our case:
If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.
So B{/*constructor of A*/}
need to construct base class A, which is protected...
add a comment |
From my understanding of https://en.cppreference.com/w/cpp/language/value_initialization
B{}
does an aggregate_initialization,
and since C++17:
The effects of aggregate initialization are:
- Each direct public base, (since C++17) [..] is copy-initialized from the corresponding clause of the initializer list.
and in our case:
If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.
So B{/*constructor of A*/}
need to construct base class A, which is protected...
add a comment |
From my understanding of https://en.cppreference.com/w/cpp/language/value_initialization
B{}
does an aggregate_initialization,
and since C++17:
The effects of aggregate initialization are:
- Each direct public base, (since C++17) [..] is copy-initialized from the corresponding clause of the initializer list.
and in our case:
If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.
So B{/*constructor of A*/}
need to construct base class A, which is protected...
From my understanding of https://en.cppreference.com/w/cpp/language/value_initialization
B{}
does an aggregate_initialization,
and since C++17:
The effects of aggregate initialization are:
- Each direct public base, (since C++17) [..] is copy-initialized from the corresponding clause of the initializer list.
and in our case:
If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.
So B{/*constructor of A*/}
need to construct base class A, which is protected...
answered Jan 8 at 14:01
Jarod42Jarod42
119k12104189
119k12104189
add a comment |
add a comment |
The final draft of C++17 n4659 has a compatibility section which contains the changes with respect to previous versions.
C.4.4 Clause 11: declarators [diff.cpp14.decl]
11.6.1
Change: Definition of an aggregate is extended to apply to user-defined types with base classes.
Rationale: To increase
convenience of aggregate initialization.
Effect on original feature:
Valid C++ 2014 code may fail to compile or produce different results
in this International Standard; initialization from an empty
initializer list will perform aggregate initialization instead of
invoking a default constructor for the affected types:
struct derived;
struct base {
friend struct derived;
private:
base();
};
struct derived : base {};
derived d1{}; // Error. The code was well-formed before.
derived d2; // still OK
I compiled the above example code with -std=c++14
and it compiled but failed to compile with -std=c++17
.
I believe that could be the reason why the code in the OP fails with B{}
but succeeds with B()
.
add a comment |
The final draft of C++17 n4659 has a compatibility section which contains the changes with respect to previous versions.
C.4.4 Clause 11: declarators [diff.cpp14.decl]
11.6.1
Change: Definition of an aggregate is extended to apply to user-defined types with base classes.
Rationale: To increase
convenience of aggregate initialization.
Effect on original feature:
Valid C++ 2014 code may fail to compile or produce different results
in this International Standard; initialization from an empty
initializer list will perform aggregate initialization instead of
invoking a default constructor for the affected types:
struct derived;
struct base {
friend struct derived;
private:
base();
};
struct derived : base {};
derived d1{}; // Error. The code was well-formed before.
derived d2; // still OK
I compiled the above example code with -std=c++14
and it compiled but failed to compile with -std=c++17
.
I believe that could be the reason why the code in the OP fails with B{}
but succeeds with B()
.
add a comment |
The final draft of C++17 n4659 has a compatibility section which contains the changes with respect to previous versions.
C.4.4 Clause 11: declarators [diff.cpp14.decl]
11.6.1
Change: Definition of an aggregate is extended to apply to user-defined types with base classes.
Rationale: To increase
convenience of aggregate initialization.
Effect on original feature:
Valid C++ 2014 code may fail to compile or produce different results
in this International Standard; initialization from an empty
initializer list will perform aggregate initialization instead of
invoking a default constructor for the affected types:
struct derived;
struct base {
friend struct derived;
private:
base();
};
struct derived : base {};
derived d1{}; // Error. The code was well-formed before.
derived d2; // still OK
I compiled the above example code with -std=c++14
and it compiled but failed to compile with -std=c++17
.
I believe that could be the reason why the code in the OP fails with B{}
but succeeds with B()
.
The final draft of C++17 n4659 has a compatibility section which contains the changes with respect to previous versions.
C.4.4 Clause 11: declarators [diff.cpp14.decl]
11.6.1
Change: Definition of an aggregate is extended to apply to user-defined types with base classes.
Rationale: To increase
convenience of aggregate initialization.
Effect on original feature:
Valid C++ 2014 code may fail to compile or produce different results
in this International Standard; initialization from an empty
initializer list will perform aggregate initialization instead of
invoking a default constructor for the affected types:
struct derived;
struct base {
friend struct derived;
private:
base();
};
struct derived : base {};
derived d1{}; // Error. The code was well-formed before.
derived d2; // still OK
I compiled the above example code with -std=c++14
and it compiled but failed to compile with -std=c++17
.
I believe that could be the reason why the code in the OP fails with B{}
but succeeds with B()
.
edited Jan 8 at 15:25
answered Jan 8 at 14:07
P.WP.W
17.6k41657
17.6k41657
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%2f54092781%2fcompilation-error-when-using-empty-list-initialization-constructor-in-c17%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
1
Try adding this flag:
-std=c++0x
. I am not sure though.– DimChtz
Jan 8 at 13:26
1
@DimChtz That changes the standard to C++11 draft, on compilers old enough to support its pre-standardisation name. Probably not helpful.
– Lightness Races in Orbit
Jan 8 at 13:27
1
@SPlatten Don't worry, you're not alone - almost forty years later many professional C++ developers still don't realise that things defined with the keyword
struct
can have constructors, member functions, access control, bases, ... (and, more reasonably, many of us still try to avoid it for purely aesthetic reasons). It must be the language's best-kept secret, but it has been so for a very, very long time. Anyway, going back to your initial comment, point is, there's nothing mystical or strange or invalid about the OP's typeA
here.– Lightness Races in Orbit
Jan 8 at 14:24
1
@SPlatten Well, C++ isn't an OO language, so that's reasonable :)
– Lightness Races in Orbit
Jan 8 at 14:26
3
@SPlatten Bjarne may originally have called it "C With Classes", and implied that this makes it OO, but he was wrong. Okay, I'll be charitable and change that to say that the generally-accepted terminology has shifted in the interim, as things were added and times changed. C++ is multi-paradigm, supporting class-based OO as an option but alternatives too (imperative, concurrent, template metaprogramming). Java has elements of some of these things but is fundamentally OO in a way that C++ never was.
– Lightness Races in Orbit
Jan 8 at 14:29