Vraag Gewijzigde regels voor beschermde constructeurs in C ++ 17?


Ik heb deze testcase:

struct A{ protected: A(){} };
struct B: A{};
struct C: A{ C(){} };
struct D: A{ D() = default; };

int main(){
    (void)B{};
    (void)C{};
    (void)D{};
}

Zowel gcc als clang compileren het in de C ++ 11 en C ++ 14 modus. Beide mislukken in C ++ 17-modus:

$ clang++ -std=c++17 main.cpp 
main.cpp:7:10: error: base class 'A' has protected default constructor
        (void)B{};
                ^
main.cpp:1:22: note: declared protected here
struct A{ protected: A(){} };
                     ^
main.cpp:9:10: error: base class 'A' has protected default constructor
        (void)D{};
                ^
main.cpp:1:22: note: declared protected here
struct A{ protected: A(){} };
                     ^
2 errors generated.

$ clang++ --version
clang version 6.0.0 (http://llvm.org/git/clang.git 96c9689f478d292390b76efcea35d87cbad3f44d) (http://llvm.org/git/llvm.git 360f53a441902d19ce27d070ad028005bc323e61)
Target: x86_64-unknown-linux-gnu
Thread model: posix

(gecompileerd vanaf master Branch 2017-12-05.)

$ g++ -std=c++17 main.cpp 
main.cpp: In function 'int main()':
main.cpp:7:10: error: 'A::A()' is protected within this context
  (void)B{};
          ^
main.cpp:1:22: note: declared protected here
 struct A{ protected: A(){} };
                      ^
main.cpp:9:10: error: 'A::A()' is protected within this context
  (void)D{};
          ^
main.cpp:1:22: note: declared protected here
 struct A{ protected: A(){} };
                      ^

$ g++ --version
g++ (GCC) 8.0.0 20171201 (experimental)
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Is deze gedragswijziging onderdeel van C ++ 17 of is het een fout in beide compilers?


58
2017-12-05 14:33


oorsprong


antwoorden:


De definitie van aggregaat veranderd sinds C ++ 17.

Voor C ++ 17

geen basisklassen

Sinds C ++ 17

Nee virtual, private, or protected (since C++17) basis klassen

Dat betekent, voor B en D, ze zijn niet geaggregeerd vóór C ++ 17, daarna voor B{} en D{}, waarde-initialisatie zal worden uitgevoerd, dan is de standaard default-constructor zal gebeld worden; dat is prima, omdat het protected constructor van de basisklasse kan worden aangeroepen door de constructor van de afgeleide klasse.

Sinds C ++ 17, B en D uitgewerkt type worden (omdat ze alleen hebben public basisklasse, en let op voor de klas D, de expliciet standaard ingestelde constructor is toegestaan ​​voor geaggregeerd type sinds C ++ 11) en vervolgens voor B{} en D{}, aggregaat-initialisatie zal worden uitgevoerd,

Elk direct public base, (since C++17) arrayelement, of niet-statisch klassenlid, in volgorde van array-subscript / uiterlijk in de klassedefinitie, wordt geïnitieerd met de kopie van de overeenkomstige clausule van de initialisatielijst.

Als het aantal initialisatieclausules kleiner is dan het aantal leden and bases (since C++17) of de initialisatorlijst is volledig leeg, de overige leden and bases (since C++17) zijn geïnitialiseerd by their default initializers, if provided in the class definition, and otherwise (since C++14) door lege lijsten, in overeenstemming met de gebruikelijke lijstinitialisatieregels (die waarde-initialisatie uitvoert voor niet-klassentypen en niet-geaggregeerde klassen met standaardconstructors, en totale initialisatie voor aggregaten). Als een lid van een referentietype een van deze overgebleven leden is, is het programma niet goed geformuleerd.

Dat betekent dat het subobject van de basisklasse rechtstreeks door de waarde wordt geïnitialiseerd, de constructor van B en D zijn overbrugd; maar de standaard constructor van A is protected, dan mislukt de code. (Let daar op A is geen aggregatietype omdat het een constructor heeft die door de gebruiker is geleverd.)

BTW: C (met een door de gebruiker geleverde constructor) is geen aggregaattype voor en na C ++ 17, dus het is in beide gevallen prima.


51
2017-12-05 15:08



In C ++ 17 zijn regels over aggregaten veranderd.

U kunt dit bijvoorbeeld nu in C ++ 17 doen:

struct A { int a; };
struct B { B(int){} };

struct C : A {};
struct D : B {};

int main() {
    (void) C{2};
    (void) D{1};
}

Merk op dat we constructor niet erven. In C ++ 17, C en D zijn nu aggregaten, zelfs als ze basisklassen hebben.

Met {}, aggregatie van initialisatie begint en verzending van geen parameters zal hetzelfde worden geïnterpreteerd als de standaard constructor van de ouder van buitenaf aanroepen.

Het initialiseren van aggregaten kan bijvoorbeeld worden uitgeschakeld door de klasse te wijzigen D hieraan:

struct B { protected: B(){} };

struct D : B {
    int b;
private:
    int c;
};

int main() {
    (void) D{}; // works!
}

Dit komt omdat de totale initialisatie niet van toepassing is wanneer leden met verschillende toegangsspecificaties zijn.

De reden waarom met = default werkt is omdat het geen door een gebruiker aangeleverde constructor is. Meer informatie op deze vraag.


22
2017-12-05 14:58