Изменены правила для защищенных конструкторов в 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 или это ошибка в обоих компиляторах?

736   2  

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

    Ничего не найдено.