Why derived constructor needs base destructor? - c++

class A{
public:
A();
private:
~A();
};
class B:public A{
public:
B(){};
private:
~B();
};
int main()
{
return 0;
}
I've got a compile error like this:
test.cpp: In constructor 'B::B()':
test.cpp:5:4: error: 'A::~A()' is private
test.cpp:10:8: error: within this context
I know the derived constructor need to invoke the base destructor, hence I set A::A() as public.
However, why the compiler complains that it need public A::~A() either?

The closest specification I could find is here - 12.4.10 class.dtor:
... A program is ill-formed if an object of class type or array
thereof is declared and the destructor for the class is not accessible
at the point of the declaration.
class B is not able to access private: ~A(), but is implicitly declaring class A because it is a base class (it is almost the same as declaring first member variable - except empty base optimization).
I am not a language lawyer and not a native speaker, but the inaccessible base class destructor seems to be the problem and the above may explain why the error points to the constructor B() (the compiler may perform the checks when they are really needed, not before).
The destructor ~A() needs to be made at least protected or B friend of A.

The C++ standards committee core working group defect report 1424 (submitted by Daniel Krügler on 2011-12-07) says:
The current specification does not appear to say whether an implementation is permitted/required/forbidden to complain when a sub-object's destructor is inaccessible.
This is fixed in C++14 by the addition of the notion of a destructor being potentially invoked. The current draft standard section 12.6.2(10) says:
In a non-delegating constructor, the destructor for each direct or virtual base class and for each non-static data member of class type is potentially invoked (12.4).
[ Note: This provision ensures that destructors can be called for fully-constructed sub-objects in case an exception is thrown (15.2). —end note ]
And in 12.4(11):
A destructor is potentially invoked if it is invoked or as specified in 5.3.4 and 12.6.2. A program is ill-formed if a destructor that is potentially invoked is deleted or not accessible from the context of the invocation.

Declare constructor and destructor as public (base class constructor anddestructor may be protected, if you ensure that is called by the subclasses only). Subclasses when constructed have base class instance created as a cubobject, so either explicit or implicit call of constructor and destructor are done

Related

Class with defaulted protected destructor not trivially-destructible but derived class is?

In the following exemple the first static assertion fired but not the second:
#include<type_traits>
struct A{
protected:
~A()=default;
};
struct B:A{
//this static assertion fails
static_assert(std::is_trivially_destructible<A>::value,"");
};
//this static assertion succeeds
static_assert(std::is_trivially_destructible<B>::value,"");
(checked with GCC,Clang,MSVC,ellcc)
I don't understand why A can not be trivially_destructible, inside B, while B is trivially destructible. This seems to be in contradiction with these 2 paragraphs of the C++ standard, where accessibility is not mentionned:
[class.dtor]
A destructor is trivial if it is not user-provided and if:
(6.1) — the destructor is not virtual,
(6.2) — all of the direct base classes of its class have trivial destructors, and
(6.3) — for all of the non-static data members of its class that are of class type (or array thereof), each such
class has a trivial destructor.
[dcl.fct.def.default]
A function is user-provided if it is user-declared and not explicitly defaulted or deleted
on its first declaration.
Simply put, because from an outside point of view, A is not destructible at all! The destructor is protected, so if you have a A* ptr, calling delete ptr will fail to compile.
To complement Sebastian Redl answer: std::is_trivially_destructible<T>::value==false does not imply that the type is not trivially-destructible.
So if one use it in template programming, it is much better not to use standard library type traits, but to directly use compiler intrinsic. For exemple with gcc:
#include<type_traits>
struct A{
protected:
~A(){};
};
struct B:A{
//Bad: fails while the fact that A is trivially destructible
//could be used to implement optimized function member in B.
static_assert(std::is_trivially_destructible<A>::value,"");
//Good: __has_trivial_destructor actualy return what is name says!!
static_assert(__has_trivial_destructor(A),"");
};

Why am I permitted to declare an object with a deleted destructor?

Consider the following text:
[C++11: 12.4/11]: Destructors are invoked implicitly
for constructed objects with static storage duration (3.7.1) at program termination (3.6.3),
for constructed objects with thread storage duration (3.7.2) at thread exit,
for constructed objects with automatic storage duration (3.7.3) when the block in which an object is created exits (6.7),
for constructed temporary objects when the lifetime of a temporary object ends (12.2),
for constructed objects allocated by a new-expression (5.3.4), through use of a delete-expression (5.3.5),
in several situations due to the handling of exceptions (15.3).
A program is ill-formed if an object of class type or array thereof is declared and the destructor for the class is not accessible at the point of the declaration. Destructors can also be invoked explicitly.
Then why does this program compile successfully?
#include <iostream>
struct A
{
A(){ };
~A() = delete;
};
A* a = new A;
int main() {}
// g++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
Is GCC just being permissive?
I'm inclined to say so, since it rejects the following yet the standard appears to have no particular rule specific to deleted destructors in an inheritance hierarchy (the only loosely relevant wording is pertinent to the generation of defaulted default constructors):
#include <iostream>
struct A
{
A() {};
~A() = delete;
};
struct B : A {};
B *b = new B; // error: use of deleted function
int main() {}
The first part is not ill-formed because the standard text doesn't apply - no object of type A is declared there.
For the second part, let's review how object construction works. The standard says (15.2/2) that if any part of construction throws, all fully constructed subobjects up to that point are destroyed in reverse order of construction.
This means that the code underlying a constructor, if all written out by hand, would look something like this:
// Given:
struct C : A, B {
D d;
C() : A(), B(), d() { /* more code */ }
};
// This is the expanded constructor:
C() {
A();
try {
B();
try {
d.D();
try {
/* more code */
} catch(...) { d.~D(); throw; }
} catch(...) { ~B(); throw; }
} catch(...) { ~A(); throw; }
}
For your simpler class, the expanded code for the default constructor (whose definition is required by the new expression) would look like this:
B::B() {
A();
try {
// nothing to do here
} catch(...) {
~A(); // error: ~A() is deleted.
throw;
}
}
Making this work for cases where no exception can possibly be thrown after initialization for some subobject has been completed is just too complex to specify. Therefore, this doesn't actually happen, because the default constructor for B is implicitly defined as deleted in the first place, due to the last bullet point in N3797 12.1/4:
A defaulted default constructor for class X is defined as deleted if:
[...]
any direct or virtual base class or non-static data member has a type with a destructor that is deleted or inaccessible from the defaulted default constructor.
The equivalent language exists for copy/move constructors as the fourth bullet in 12.8/11.
There is also an important paragraph in 12.6.2/10:
In a non-delegating constructor, the destructor for each direct or virtual base class and for each non-static data member of class type is potentially invoked.
It's that B's destructor is generated by the compiler at the line of your error and it has a call to A's destructor which is deleted, hence the error. In the first example nothing is trying to call A's destructor hence no error.
My guess is that this is what happens.
The implicitly generated B() constructor will first of all construct its base class subobject of type A. The language then states that if an exception is thrown during execution of the body of the B() constructor, the A subobject must be destroyed. Hence the need to access the deleted ~A() - it is formally needed for when the constructor throws. Of course, since the generated body of B() is empty this can never ever happen, but the requirement that ~A() should be accessible is still there.
Of course, this is 1) just a guess from my side of why there is an error in the first place and 2) not in any way a quote of the standardese to say whether this would actually be formally ill-formed or just an implementation detail in gcc. Could perhaps give you a clue of where in the standard to look though...
Accessibility is orthogonal to deletedness:
[C++11: 11.2/1]: If a class is declared to be a base class (Clause 10) for another class using the public access specifier, the public members of the base class are accessible as public members of the derived class and protected members of the base class are accessible as protected members of the derived class. If a class is declared to be a base class for another class using the protected access specifier, the public and protected members of the base class are accessible as protected members of the derived class. If a class is declared to be a base class for another class using the private access specifier, the public and protected members of the base class are accessible as private members of the derived class.
There is this:
[C++11: 8.4.3/2]: A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed. [ Note: This includes calling the function implicitly or explicitly and forming a pointer or pointer-to-member to the function. It applies even for references in expressions that are not potentially-evaluated. If a function is overloaded, it is referenced only if the function is selected by overload resolution. —end note ]
But you never "refer to" the deleted destructor.
(I still can't explain why the inheritance example doesn't compile.)

Do I need to implement my own destructors for my derived classes if they don't contain static data?

Let's say I have:
class Base {
public:
virtual ~Base() = 0;
}
class Derived: public Base {
public:
~Derived();
}
In my code I need to delete Derived classes through a Base pointer, like this:
delete[] pt_base;
However, I don't actually have anything to destroy in the implemented destructors. But I still need them to delete through the base class?
What's the best way to achieve what I want without a whole lot of wasted code and empty destructors?
No, virtual destructor declaration/definition of the Base class is sufficient, though making it abstract requires you give this destructor in the Derived class.
If you just declare
class Base {
public:
virtual ~Base() {} // <<< Have a definition
};
you don't need to specify any destructors in inherited classes.
If you want to provide a destructor with a "default" implementation, then the default keyword is provided for precisely this purpose. It results in a user-declared destructor that has the same behaviours as an implicitly-defined one.
class Derived : public Base {
public:
~Derived() = default;
}
This is equivalent to providing an empty destructor body like {}, but may better display your intent.
But, no, you don't need to provide these destructors:
[C++11: 12.4/4]: If a class has no user-declared destructor, a destructor is implicitly declared as defaulted (8.4). An implicitly-declared destructor is an inline public member of its class.
[C++11: 12.4/5]: A defaulted destructor for a class X is defined as deleted if:
X is a union-like class that has a variant member with a non-trivial destructor,
any of the non-static data members has class type M (or array thereof) and M has a deleted destructor or a destructor that is inaccessible from the defaulted destructor,
any direct or virtual base class has a deleted destructor or a destructor that is inaccessible from the defaulted destructor,
or, for a virtual destructor, lookup of the non-array deallocation function results in an ambiguity or in a function that is deleted or inaccessible from the defaulted destructor.
[..]
None of these rules apply here, and no rule exists to change this behaviour in the case that a base class has a pure or impure virtual destructor, so the implicitly-declared destructor in my example above is not deleted (i.e. valid).
The following wording is another strong clue that the derived destructor can be declared for you:
[C++11: 12.4/9]: A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any derived class are created in the program, the destructor shall be defined. If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly-declared) is virtual.
Unfortunately you won't be able to use default for the Base class if you want the destructor to be pure; you'll still have to provide its [empty] definition separately:
class Base {
public:
virtual ~Base() = 0;
};
Base::~Base() {}

Virtual inheritance vs. non-default constructors

This code is rejected by (at least) MSVC, ICC, and GCC:
class A {
public:
A( int ) { }
};
class B: virtual public A {
public:
//B(): A( -1 ) { } // uncomment to make it compilable
virtual void do_something() = 0;
};
class C: public B {
public:
C(): A( 1 ) { }
virtual void do_something() { }
};
int main() {
C c;
return 0;
}
on the basis of
error : no default constructor exists for class "A"
class B: virtual public A {
^
detected during implicit generation of "B::B()" at line 14
Questions:
If the code is indeed invalid, how exactly does this follow from
the standard? AFAICT, 10.4/2 and 1.8/4 taken together imply that B
cannot be a type of the most derived class, and therefore from
12.6.2/10 we have that B can never, ever call A's constructors.
(The section numbers are for C++11.)
If the code is valid, are compilers violating the standard by
requiring the presence of constructors they could not possibly call?
Note that not only they want to call A::A() from B::B(), but they
want to do it while compiling C::C() (double weird).
P.S. This was originally asked on the ICC forum, but posted here due to not being limited to this compiler (and no details forthcoming).
Clang shows the error as:
error: call to implicitly-deleted default constructor of 'B'
C(): A( 1 ) { }
^
12.1/5 says "A defaulted default constructor for class X is defined as deleted if [...] any [...] virtual base class [...] has class type M [...] and [...] M has no default constructor [...]."
I think you are trying to derive a "theorem" from the facts that can be found in the standard, and then you expect the standard to acknowledge the existence of that "theorem". The standard does not do that. It doesn't strive to find and incorporate all possible "theorems" that can be derived from the standard text.
Your "theorem" is perfectly valid (unless I'm missing something). You are right, since class B is abstract, this class can never be used as a most derived class. This immediately means that class B will never get a chance to construct its virtual base A. And that means that technically in B the compiler should not care about the availability, and/or accessibility of the appropriate constructors in A or in any other virtual bases.
But the standard simply does not make that connection and does not care to make it. It doesn't treat constructors of abstract classes in any special way. The requirements imposed on such constructors are the same as for non-abstract classes.
You can call try suggesting it as a possible improvement to the standard committee.
It looks like 12.6.2/4 prohibits this to me:
If a given nonstatic data member or base class is not named by a
mem-initializer-id (including the case where there is no
mem-initializer-list because the constructor has no ctor-initializer),
then
— If the entity is a nonstatic data member of (possibly
cv-qualified) class type (or array thereof) or a base class, and the
entity class is a non-POD class, the entity is default-initialized
(8.5)...
It looks to me like that regardless of the class being a virtual base, B's default constructor is still synthesized by the compiler, and such default constructor doesn't know how to construct its A base ("the entity is default-initialized (8.5)").

MSVC9.0 bug or misunderstanding of virtual inheritance and friends?

consider the following code:
class A
{
friend class B;
friend class C;
};
class B: virtual private A
{
};
class C: private B
{
};
int main()
{
C x; //OK default constructor generated by compiler
C y = x; //compiler error: copy-constructor unavailable in C
y = x; //compiler error: assignment operator unavailable in C
}
The MSVC9.0 (the C++ compiler of Visual Studio 2008) does generate the default constructor but is unable to generate copy and assignment operators for C although C is a friend of A. Is this the expected behavior or is this a Microsoft bug? I think the latter is the case, and if I am right, can anyone point to an article/forum/... where this issue is discussed or where microsoft has reacted to this bug. Thank you in advance.
P.S. Incidentally, if BOTH private inheritances are changed to protected, everything works
P.P.S. I need a proof, that the above code is legal OR illegal. It was indeed intended that a class with a virtual private base could not be derived from, as I understand. But they seem to have missed the friend part. So... here it goes, my first bounty :)
The way I interpret the Standard, the sample code is well-formed. (And yes, the friend declarations make a big difference from the thing #Steve Townsend quoted.)
11.2p1: If a class is declared to be a base class for another class using the private access specifier, the public and protected members of the base class are accessible as private members of the derived class.
11.2p4: A member m is accessible when named in class N if
m as a member of N is public, or
m as a member of N is private, and the reference occurs in a member or friend of class N, or
m as a member of N is protected, and the reference occurs in a member or friend of class N, or in a member or friend of a class P derived from N, where m as a member of P is private or protected, or
there exists a base class B of N that is accessible at the point of reference, and m is accessible when named in class B.
11.4p1: A friend of a class is a function or class that is not a member of the class but is permitted to use the private and protected member names from the class.
There are no statements in Clause 11 (Member access control) which imply that a friend of a class ever has fewer access permissions than the class which befriended it. Note that "accessible" is only defined in the context of a specific class. Although we sometimes talk about a member or base class being "accessible" or "inaccessible" in general, it would be more accurate to talk about whether it is "accessible in all contexts" or "accessible in all classes" (as is the case when only public is used).
Now for the parts which describe checks on access control in automatically defined methods.
12.1p7: An implicitly-declared default constructor for a class is implicitly defined when it is used to create an object of its class type (1.8). The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with an empty mem-initializer-list (12.6.2) and an empty function body. If that user-written default constructor would be ill-formed, the program is ill-formed.
12.6.2p6: All sub-objects representing virtual base classes are initialized by the constructor of the most derived class (1.8). If the constructor of the most derived class does not specify a mem-initializer for a virtual base class V, then V's default constructor is called to initialize the virtual base class subobject. If V does not have an accessible default constructor, the initialization is ill-formed.
12.4p5: An implicitly-declared destructor is implicitly defined when it is used to destroy an object of its class type (3.7). A program is ill-formed if the class for which a destructor is implicitly defined has:
a non-static data member of class type (or array thereof) with an inaccessible destructor, or
a base class with an inaccessible destructor.
12.8p7: An implicitly-declared copy constructor is implicitly defined if it is used to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type. [Note: the copy constructor is implicitly defined even if the implementation elided its use (12.2).] A program is ill-formed if the class for which a copy constructor is implicitly defined has:
a nonstatic data member of class type (or array thereof) with an inaccessible or ambiguous copy constructor, or
a base class with an inaccessible or ambiguous copy constructor.
12.8p12: A program is ill-formed if the class for which a copy assignment operator is implicitly defined has:
a nonstatic data member of const type, or
a nonstatic data member of reference type, or
a nonstatic data member of class type (or array thereof) with an inaccessible copy assignment operator, or
a base class with an inaccessible copy assignment operator.
All these requirements mentioning "inaccessible" or "accessible" must be interpreted in the context of some class, and the only class that makes sense is the one for which a member function is implicitly defined.
In the original example, class A implicitly has public default constructor, destructor, copy constructor, and copy assignment operator. By 11.2p4, since class C is a friend of class A, all those members are accessible when named in class C. Therefore, access checks on those members of class A do not cause the implicit definitions of class C's default constructor, destructor, copy constructor, or copy assignment operator to be ill-formed.
Your code compiles fine with Comeau Online, and also with MinGW g++ 4.4.1.
I'm mentioning that just an "authority argument".
From a standards POV access is orthogonal to virtual inheritance. The only problem with virtual inheritance is that it's the most derived class that initializes the virtually-derived-from class further up the inheritance chain (or "as if"). In your case the most derived class has the required access to do that, and so the code should compile.
MSVC also has some other problems with virtual inheritance.
So, yes,
the code is valid, and
it's an MSVC compiler bug.
FYI, that bug is still present in MSVC 10.0. I found a bug report about a similar bug, confirmed by Microsoft. However, with just some cursory googling I couldn't find this particular bug.
Classes with virtual private base should not be derived from, per this at the C++ SWG. The compiler is doing the right thing (up to a point). The issue is not with the visibility of A from C, it's that C should not be allowed to be instantiated at all, implying that the bug is in the first (default) construction rather than the other lines.
Can a class with a private virtual base class be derived from?
Section: 11.2 [class.access.base]
Status: NAD Submitter: Jason
Merrill Date: unknown
class Foo { public: Foo() {} ~Foo() {} };
class A : virtual private Foo { public: A() {} ~A() {} };
class Bar : public A { public: Bar() {} ~Bar() {} };
~Bar() calls
~Foo(), which is ill-formed due to
access violation, right? (Bar's
constructor has the same problem since
it needs to call Foo's constructor.)
There seems to be some disagreement
among compilers. Sun, IBM and g++
reject the testcase, EDG and HP accept
it. Perhaps this case should be
clarified by a note in the draft. In
short, it looks like a class with a
virtual private base can't be derived
from.
Rationale: This is what was intended.
btw the Visual C++ v10 compiler behaviour is the same as noted in the question. Removing virtual from the inheritance of A in B fixes the problem.