61 votes

Règles modifiées pour les constructeurs protégés en C ++17?

J'ai ce cas de test:

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

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

Gcc et clang le compilent en mode C ++ 11 et C ++ 14. Les deux échouent en mode C ++ 17:

 $ 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
 

(clang compilé à partir de la branche principale 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.
 

Ce changement de comportement fait-il partie de C ++ 17 ou s'agit-il d'un bogue dans les deux compilateurs?

52voto

songyuanyao Points 2265

La définition de l' agrégat changé depuis C++17.

Avant Le C++17

pas de classes de base

Depuis C++17

pas de virtual, private, or protected (since C++17) classes de base

Cela signifie que, pour B et D, ils ne sont pas type de regroupement avant le C++17, puis pour B{} et D{}, la valeur d'initialisation sera effectuée, puis le défaut de constructeur par défaut sera appelé à le faire, ce qui est bien, parce que l' protected constructeur de la classe de base peut être appelée par le constructeur de la classe dérivée.

Depuis C++17, B et D devenir type de regroupement (car ils n'ont qu' public classe de base, et remarque que pour la classe D, de façon explicite par défaut dans le constructeur par défaut est autorisée pour le type de regroupement depuis C++11), puis pour B{} et D{}, ensemble-l'initialisation sera effectué à l'

Chaque direct public base, (since C++17) d'élément de tableau ou de non-statique de la classe membre, dans l'ordre d'indice de tableau/aspect dans la définition de classe, la copie est initialisé à partir de la clause correspondante de la liste d'initialiseur.

Si le nombre d'initialiseur de clauses est inférieur au nombre de membres and bases (since C++17) ou liste d'initialiseur est complètement vide, les membres restants and bases (since C++17) sont initialisés by their default initializers, if provided in the class definition, and otherwise (since C++14) par vide listes, en conformité avec la liste habituelle-initialisation des règles (qui joue de la valeur d'initialisation pour les non-types de classe et de non-agrégation des classes avec des constructeurs par défaut, et d'agrégation d'initialisation pour les agrégats). Si un membre d'un type de référence est l'un de ces membres, le programme est mal formé.

Cela signifie que la classe de base sous-objet sera la valeur initialisé directement, le constructeur de B et D sont contournés; mais le constructeur par défaut de A est protected, alors le code ne fonctionne pas. (À noter qu' A n'est pas le type de regroupement, car il a un utilisateur fourni par le constructeur.)

BTW: C (avec un nom d'utilisateur fourni par le constructeur) n'est pas un type de regroupement avant et après C++17, il est donc très bien pour les deux cas.

22voto

Guillaume Racicot Points 1106

En C++17, les règles sur les agrégats a changé.

Par exemple, vous pouvez le faire en C++17 maintenant:

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

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

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

Notez que nous ne sommes pas hériter de constructeur. En C++17, C et D sont maintenant des agrégats, même s'ils ont des classes de base.

Avec {}, agrégé de l'initialisation des coups de pied dans, et de l'envoi n'a pas de paramètres seront interprétées de la même que l'appel de la mère du constructeur par défaut de l'extérieur.

Par exemple, les agrégats d'initialisation peut être désactivé par l'évolution de la classe D de cette:

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

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

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

C'est parce que l'agrégat d'initialisation ne s'appliquent pas lors de l'avoir des membres avec différents spécificateurs d'accès.

La raison pour laquelle avec = default fonctionne est parce que ce n'est pas un utilisateur fourni constructeur. Plus d'informations sur cette question.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X