Zmienione reguły dla konstruktorów chronionych w C++17?
Mam ten przypadek testowy:
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 i clang kompilują go w trybie C++11 i C++14. Oba nie działają w trybie 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
[[3]}(clang skompilowany z 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.
Czy ta zmiana zachowania jest częścią C++17 czy jest to błąd w obu kompilatorach?
2 answers
Definicja agregatu zmieniła się od C++17.
Przed C++17
Brak klas bazowych
Od C++17
Nie
virtual, private, or protected (since C++17)
klasy bazowe
Oznacza to, że dla B
i D
nie są typem zbiorczym przed C++17, wtedy dla B{}
i D{}
, wartość-inicjalizacja zostanie wykonana, wtedy zostanie wywołany defaulted default constructor; co jest w porządku, ponieważ protected
konstruktor klasa bazowa może być wywołana przez konstruktor klasy pochodnej.
Od C++17, B
i D
stają się typem agregatowym (ponieważ mają tylko public
klasę bazową i zauważ, że dla klasy D
jawnie domyślny konstruktor jest dozwolony dla typu agregatowego od C++11), następnie dla B{}
i D{}
, zostanie wykonana inicjalizacja zbiorcza,
Każdy
direct public base, (since C++17)
element tablicy, lub niestatyczny członek klasy, w kolejności indeksowania/wyglądu tablicy w klasie definicja, jest kopiowana z odpowiedniej klauzuli listy inicjującej.Jeśli liczba klauzul inicjalizacyjnych jest mniejsza niż liczba członków
and bases (since C++17)
lub lista inicjalizacyjna jest całkowicie pusta, pozostałe elementy {[13] } są inicjalizowaneby their default initializers, if provided in the class definition, and otherwise (since C++14)
pustymi listami, zgodnie ze zwykłymi regułami inicjalizacji listy (które wykonują inicjalizację wartości dla typów nieklasowych i klas nie-zagregowanych z domyślnymi konstruktorami oraz inicjalizację zbiorczą dla typów Agregaty). Jeśli członek typu referencyjnego jest jednym z tych pozostałych członków, program jest źle uformowany.
To oznacza, że obiekt podrzędny klasy bazowej zostanie zainicjowany bezpośrednio, konstruktor B
i D
zostanie pominięty; ale domyślnym konstruktorem A
jest protected
, wtedy kod nie powiedzie się. (Zauważ, że A
nie jest typem agregowanym, ponieważ ma konstruktor dostarczony przez użytkownika.)
BTW: C
(z konstruktorem dostarczonym przez użytkownika) nie jest typem agregatu przed i po C++17, więc jest w porządku w obu przypadkach.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-06 00:33:45
W C++17 zasady dotyczące agregatów uległy zmianie.
Na przykład, możesz to zrobić w C++17 teraz:
struct A { int a; };
struct B { B(int){} };
struct C : A {};
struct D : B {};
int main() {
(void) C{2};
(void) D{1};
}
Zauważ, że nie dziedziczymy konstruktora. W C++17, C
i D
są teraz agregatami, nawet jeśli mają klasy bazowe.
Z {}
rozpoczyna się inicjalizacja zbiorcza i wysyłanie żadnych parametrów będzie interpretowane tak samo jak wywołanie domyślnego konstruktora rodzica z zewnątrz.
Na przykład inicjalizacja zbiorcza może zostać wyłączona poprzez zmianę klasa D
do tego:
struct B { protected: B(){} };
struct D : B {
int b;
private:
int c;
};
int main() {
(void) D{}; // works!
}
Dzieje się tak dlatego, że inicjalizacja zbiorcza nie ma zastosowania w przypadku członków o różnych specyfikacjach dostępu.
Powodem, dla którego z = default
działa, jest to, że nie jest konstruktorem dostarczonym przez użytkownika. Więcej informacji na to pytanie .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-05 15:20:47