Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changed rules for protected constructors in C++17?

I have this test case:

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

Both gcc and clang compile it in C++11 and C++14 mode. Both fail in C++17 mode:

$ 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 compiled from 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. 

Is this change of behavior part of C++17 or is it a bug in both compilers?

like image 478
Benjamin Buch Avatar asked Dec 05 '17 14:12

Benjamin Buch


People also ask

What happens if a constructor is declared protected?

Protecting a constructor prevents the users from creating the instance of the class, outside the package. During overriding, when a variable or method is protected, it can be overridden to other subclass using either a public or protected modifier only. Outer class and interface cannot be protected.

Can we use protected in constructor?

Modifiers public, protected and, private are allowed with constructors. We can use a private constructor in a Java while creating a singleton class.

What happens if we declare constructor as private in CPP?

Constructor may be private. Show activity on this post. In your code, the program cannot run since you have defined a constructor and it is private. Therefore, in your current code, there is no way to create objects of the class, making the class useless in a sense.

Can constructors be declared in protected section of the class?

Constructors cannot be declared in protected section of the class.


1 Answers

The definition of aggregate changed since C++17.

Before C++17

no base classes

Since C++17

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

That means, for B and D, they're not aggregate type before C++17, then for B{} and D{}, value-initialization will be performed, then the defaulted default constructor will be called; which is fine, because the protected constructor of base class could be called by derived class's constructor.

Since C++17, B and D become aggregate type (because they have only public base class, and note that for class D, the explicitly defaulted default constructor is allowed for aggregate type since C++11), then for B{} and D{}, aggregate-initialization will be performed,

Each direct public base, (since C++17) array element, or non-static class member, in order of array subscript/appearance in the class definition, is copy-initialized from the corresponding clause of the initializer list.

If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.

That means the base class subobject will be value-initialized directly, the constructor of B and D are bypassed; but the default constructor of A is protected, then the code fails. (Note that A is not aggregate type because it has a user-provided constructor.)

BTW: C (with a user-provided constructor) is not an aggregate type before and after C++17, so it's fine for both cases.

like image 119
songyuanyao Avatar answered Sep 22 '22 07:09

songyuanyao