class B {
private:
friend class C;
B() = default;
};
class C : public B {};
class D : public B {};
int main() {
C {};
D {};
return 0;
}
I assumed that since only class C
is a friend of B
, and B
's constructor is private, then only class C
is valid and D
is not allowed to instantiate B
. But that's not how it works. Where am I wrong with my reasoning, and how to achieve this kind of control over which classes are allowed to subclass a certain base?
Update: as pointed out by others in the comments, the snippet above works as I initially expected under C++14, but not C++17. Changing the instantiation to C c; D d;
in main()
does work as expected in C++17 mode as well.
If a class has one or more private constructor and no public constructor then other classes are not allowed to create instance of this class; this means you can neither create the object of the class nor can it be inherited by other classes.
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.
You can't inherit from a base-class whose only constructor is private. So make the base-class constructor public/protected, or add another base-class constructor.
Members of a class that are declared private are not inherited by subclasses of that class. Only members of a class that are declared protected or public are inherited by subclasses declared in a package other than the one in which the class is declared. The answer is No. They do not.
This is a new feature added to C++17. What is going on is C
is now considered an aggregate. Since it is an aggregate, it doesn't need a constructor. If we look at [dcl.init.aggr]/1 we get that an aggregate is
An aggregate is an array or a class with
no user-provided, explicit, or inherited constructors ([class.ctor]),
no private or protected non-static data members (Clause [class.access]),
no virtual functions, and
no virtual, private, or protected base classes ([class.mi]).
[ Note: Aggregate initialization does not allow accessing protected and private base class' members or constructors. — end note ]
And we check of all those bullet points. You don't have any constructors declared in C
or D
so there is bullet 1. You don't have any data members so the second bullet doesn't matter, and your base class is public so the third bullet is satisfied.
The change that happened between C++11/14 and C++17 that allows this is that aggregates can now have base classes. You can see the old wording here where it expressly stated that bases classes are not allowed.
We can confirm this by checking the trait std::is_aggregate_v
like
int main()
{
std::cout << std::is_aggregate_v<C>;
}
which will print 1.
Do note that since C
is a friend of B
you can use
C c{};
C c1;
C c2 = C();
As valid ways to initialize a C
. Since D
is not a friend of B
the only one that works is D d{};
as that is aggregate initialization. All of the other forms try to default initialize and that can't be done since D
has a deleted default constructor.
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