Consider:
struct B {
void f();
private:
B(int, int = 0);
};
struct D : B { using B::B; };
void B::f() {
auto a = D{0};
auto b = D(0);
auto c = D(0, 0);
D x{0};
D y(0);
D z(0, 0);
}
GCC accepts (since 7.1; previously rejects all).
Clang accepts b
and xyz
but rejects a
and c
.
MSVC rejects all in C++14 mode but accepts all in C++17 mode.
Which compiler(s) are correct? Did the rules change between C++14 and C++17 - if so, why in C++14 is B::f
not allowed to access its own constructors (named via D
)? And why does Clang only accept the (function-style) cast at b
and not list-initialization at a
or constructor call at c
?
(Making B
a friend
of D
makes Clang accept, except for older versions (3.8 and older), and MSVC in C++14 mode. It doesn't make a difference to gcc.)
Now, I know that C++14 says:
A constructor so declared [as an inheriting constructor] has the same access as the corresponding constructor in [the base class] X.
And in C++17 the rules were simplified, clarified and moved to [namespace.udecl]:
A using-declarator 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.
So, I thought that perhaps "has the same access as" means the inherited constructors are private
to D
so only accessible to D
(and its friends), rather than private
to B
(and so accessible to B::f
) as are their corresponding constructors in B
. But under that interpretation, one would be able to subvert access checking and access private constructors of one's base class by inheriting them. So surely in C++14 the intent of the wording is the same as expressed more clearly in C++17 - but then why does MSVC change its behavior depending on language version?
Private members of the base class cannot be used by the derived class unless friend declarations within the base class explicitly grant access to them.
You can directly access the private member via its address in memory. If you're comfortable with it that is. You could have a function in the base class that returns the address of the private member and then use some wrapping function in the derived class to retrieve, dereference and set the private member.
In inheritance, the derived class inherits all the members(fields, methods) of the base class, but derived class cannot inherit the constructor of the base class because constructors are not the members of the class.
Java doesn't prevent sub-classing of class with private constructors. What it prevents is sub-classes which cannot access any constructors of its super class. This means a private constructor cannot be used in another class file, and a package local constructor cannot be used in another package.
There were a lot of issues with the original specification of inheriting constructors (as they were then called), several of which involved access control. They were fixed retroactively, which is supposed to be official for C++14 only (as the most recently published standard at the time). Many compilers choose to apply such changes to even earlier versions where it makes sense (i.e., C++11).
However, it wasn’t possible to access private base-class constructors even in C++14 as published, because the implicit definition of an inheriting private constructor would fail to invoke the actual private constructor in the base. MSVC is therefore correct except for not applying the defect report to C++14; Clang appears to have a separate bug in handling certain kinds of initialization; perhaps it accepts D(0)
because it’s defined in terms of D …(0);
. Current GCC is correct in all cases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With