Inherited constructors, default constructor and visibility

969 Views Asked by At

As stated by [namespace.udecl]/18:

[...] A using-declaration that names a constructor does not create a synonym; instead, the additional constructors are accessible if they would be accessible when used to construct an object of the corresponding base class, and the accessibility of the using-declaration is ignored. [...]

Because of that, the following code does not compile:

class B { protected: B(int) { } };
class D: B { using B::B; };
int main () { D d{0}; }

It returns an error that is more or less the same with all the major compilers:

declared protected here

On the other side, the following code compiles:

class B { protected: B() { } };
class D: B { using B::B; };
int main () { D d{}; }

Shouldn't it fail to compile instead for the same reasons that lead to an error in the previous example?
What does it allow it to compile?

2

There are 2 best solutions below

2
On BEST ANSWER

For the 2nd case, inheriting constructor doesn't take effect. According to the rules of deleted implicitly-declared default constructor, that in the 2nd case class D doesn't violate (there's a well-formed B::B() for D); the compiler will declare a default constructor as an inline public member for D, which makes D d{}; work well.

...

T has a direct or virtual base which has a deleted default constructor, or it is ambiguous or inaccessible from this constructor.

...

For the 1st case, inheriting constructors takes effect:

(emphasis mine)

If overload resolution selects an inherited constructor, it is accessible if it would be accessible when used to construct an object of the corresponding base class: the accessibility of the using-declaration that introduced it is ignored.

If overload resolution selects one of the inherited constructors when initializing an object of such derived class, then the Base subobject from which the constructor was inherited is initialized using the inherited constructor, and all other bases and members of Derived are initialized as if by the defaulted default constructor (default member initializers are used if provided, otherwise default initialization takes place).

Then it fails because of the access isolation.

0
On
class B { protected: B() { } };
class D: B { using B::B; };
int main () { D d{}; }

D has no user-defined constructor in this case, so the compiler generates one (public) for you that calls B::B (but not because of the using, that has no effect in this case), that compiler-generated constructor is then called by main.

class B { protected: B(int) { } };
class D: B { using B::B; };
int main () { D d{0}; }

Even though D has no user-defined constructor here, the compiler-generated one is implicitly deleted because B only has a constructor that takes an int. D does also have a constructor that takes an int (using did that) but this constructor is marked protected and thus inaccessible by main.