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?

Author: songyuanyao, 2017-12-05

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ą inicjalizowane by 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.

 51
Author: songyuanyao,
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 .

 22
Author: Guillaume Racicot,
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