Comeau, g++ (ideone) and EDG accept the following code without diagnostic. Visual C++ compiles successfully, albeit with warning C4624.
class indestructible_base
{
~indestructible_base();
};
class T : indestructible_base
{
public:
//T() {}
};
int main(void) { new T(); }
Uncomment the constructor and it no longer compiles.
Perhaps it's the rule that if an exception occurs inside the constructor, subobjects must be destroyed? Seems odd, since the body is empty and can't cause an exception. Even so, add an exception-specification vouching for the fact that no exception will be thrown (throw()
or noexcept
) and it makes no difference.
Why does a user-declared constructor require access to the base class destructor, while an automatically-generated constructor does not?
This question was inspired by: Preventing a Destructor from Running in C++
I suspect that this might be compiler-specific behavior. Here's my theory:
Because (in this particular case) an implicitly-defined T() is a trivial constructor (as defined in 12.1(5) of the standard), the compiler doesn't even attempt to generate a body for T(). Since there's no ctor body, there are no exceptions that could possibly be generated during "construction" (of which there isn't any, really), so there's no need to generate a dtor call, and so no need to generate a dtor body, only to find out that the base class's dtor is private.
But as soon as T() becomes non-trivial (even if it remains implicitly-defined), a ctor body must be generated, and you get the error. Something as simple as adding a member to class T that has a user-defined constructor would make the implicitly-defined T() become non-trivial.
A separate, but related, issue is that new T()
doesn't generate a dtor call (since you don't have a corresponding delete
anywhere). In contrast, if I just replace new T()
with T dummy
in your code, then I get the following from gcc
, suggesting that it's now doing the full check for dtor accessibility (as a consequence of having to generate a dtor call):
test.cpp: In destructor 'T::~T()':
test.cpp:3: error: 'indestructible_base::~indestructible_base()' is private
test.cpp:7: error: within this context
test.cpp: In function 'int main()':
test.cpp:12: note: synthesized method 'T::~T()' first required here
test.cpp:12: warning: unused variable 'dummy'
Well, if the automatically-generated constructor calls a possibly-throwing constructor, then it will give the same access error.
#include <string>
class indestructible_base
{
~indestructible_base();
std::string s; // <------ this may throw
};
class T : indestructible_base
{
public:
//T() {}
};
int main(void) { new T(); }
So I guess exceptions is the answer. In ANSI ISO IEC 14882 the only noexcept(true)
string constructor is the move constructor. I believe this should compile but ideone says no.