Изменены правила для защищенных конструкторов в C++17?
у меня есть этот тест:
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 и clang компилируют его в режиме C++11 и C++14. Оба сбоя в режиме 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 скомпилирован из 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.
является ли это изменение поведения частью C++17 или это ошибка в обоих компиляторах?
2 ответов:
определение совокупность изменено с C++17.
Перед C++17
нет базовых классов
С C++17
нет
virtual, private, or protected (since C++17)базовые классыэто означает, что для
BиD, они не являются агрегатным типом до C++17, а затем дляB{}иD{},значением-инициализации будет выполнено, то дефолт по умолчанию конструктор будет называться; что хорошо, потому чтоprotectedконструктор базового класса может быть вызван конструктор производного класса.Начиная С C++17,
BиDстать агрегатным типом (потому что у них есть толькоpublicбазовый класс, и обратите внимание, что для классаD, явно дефолтный конструктор по умолчанию разрешен для агрегатного типа С C++11), затем дляB{}иD{},агрегатно-инициализации будет исполнено,каждого
direct public base, (since C++17)элемент массива, или нестатический член класса, в порядке индекса массива / внешнего вида в определении класса, инициализируется копированием из соответствующего предложения списка инициализаторов.если число предложений инициализатора меньше числа членов
and bases (since C++17)или список инициализаторов полностью пуст, остальные членыand bases (since C++17)инициализируютсяby their default initializers, if provided in the class definition, and otherwise (since C++14)по пустым спискам, в соответствии с обычным правила инициализации списка (который выполняет инициализацию значений для неклассных типов и неагрегатных классов с конструкторами по умолчанию и инициализацию агрегатов для агрегатов). Если элемент ссылочного типа является одним из этих оставшихся элементов, программа плохо сформирована.это означает, что субобъект базового класса будет инициализирован непосредственно значением, конструктором
BиDобходятся; но конструктор по умолчаниюAиprotected, тогда код не работает. (Обратите внимание, чтоAне является агрегатным типом, поскольку он имеет пользовательский конструктор.)кстати:
C(С предоставленным пользователем конструктором) не является агрегатным типом до и после C++17, поэтому это нормально для обоих случаев.
в C++17, правила о aggregates изменилось.
например, вы можете сделать это в C++17 теперь:
struct A { int a; }; struct B { B(int){} }; struct C : A {}; struct D : B {}; int main() { (void) C{2}; (void) D{1}; }обратите внимание, что мы не наследует конструктор. В C++17,
CиDТеперь агрегаты, даже если они имеют базовые классы.С
{}, начинается инициализация aggregate, и отправка параметров не будет интерпретироваться так же, как вызов родительского конструктора по умолчанию извне.например, совокупный инициализацию можно отключить, изменив класс
Dдля этого:struct B { protected: B(){} }; struct D : B { int b; private: int c; }; int main() { (void) D{}; // works! }это связано с тем, что агрегатная инициализация не применяется при наличии членов с разными спецификаторами доступа.
поэтому с
= defaultработает, потому что это не предусмотрено конструктором. Дополнительная информация на этот вопрос.
Comments