What is hidden slicing copy constructor? - c++

This question is because of this question and the comments.
This example :
#include <iostream>
struct A {
A(int value) : m_value(value) { }
int m_value;
};
struct B : A {
B(int value) : A (value) { }
};
int main()
{
try {
throw B(5);
}
catch(A) {
std::cout << "A catch" << std::endl;
}
catch(B) {
std::cout << "B catch" << std::endl;
}
}
when compiled using g++ 4.6.1 like this :
g++ exception_slicing.cpp -ansi -pedantic -Wall -Wextra
produces next output :
exception_slicing.cpp: In function 'int main()':
exception_slicing.cpp:20:5: warning: exception of type 'B' will be caught [enabled by default]
exception_slicing.cpp:17:5: warning: by earlier handler for 'A' [enabled by default]
and the output is A catch.
I understand that the 1st catch block triggered because of the slicing problem.
Where does it say about hidden copy constructor in the base class?
Where does it say about this behaviour?
PS1 Please provide answers with quote from the standard.
PS2 And I am aware that the exceptions should be handled by const reference.

In your case, same warning pops up even if you catch by const reference, where no slicing occurs. Your problem is that since B is a public subclass of A ==> every B is-a A, so it can be caught by the first handler. You should probably order handlers from most specific to least specific.
Also, you're printing "A" in both catch blocks.

1 Where does it say about hidden copy constructor in the base class?
It's not hidden as much as it is implicit.
Using n3290:
§ 12 Special member functions
1/ The default constructor (12.1), copy constructor and copy assignment operator (12.8), move constructor and move assignment operator (12.8), and destructor (12.4) are special member functions. [ Note: The implementation will implicitly declare these member functions for some class types when the program does not explicitly declare them. The implementation will implicitly define them if they are odr-used (3.2). See 12.1, 12.4 and 12.8. —end note ]
So, let us follow the pointer:
§ 12.8 Copying and moving class objects
7/ If the class definition does not explicitly declare a copy constructor, one is declared implicitly. [...]
8/ The implicitly-declared copy constructor for a class X will have the form
X::X(const X&)
if
— each direct or virtual base class B of X has a copy constructor whose first parameter is of type const B& or const volatile B&, and
— for all the non-static data members of X that are of a class type M (or array thereof), each such class type has a copy constructor whose first parameter is of type const M& or const volatile M&.
Otherwise, the implicitly-declared copy constructor will have the form
X::X(X&)
And there you have it. In your case, there is a copy constructor A::A(A const&) implicitly defined for you.
2 Where does it say about this behaviour?
Unsuprisingly, in the part devoted to exception handling.
§ 15.3 Handling an exception
3/ A handler is a match for an exception object of type E if
[...]
— the handler is of type cv T or cv T& and T is an unambiguous public base class of E, or
[...]
It is very similar to parameter passing in functions. Because B publicly inherits from A, an instance of B can be passed as a A const&. Since a copy constructor is not explicit (hum...), B is convertible into a A, and therefore, as for functions, a B can be passed where a A (no reference) is expected.
The Standard goes on:
4/ The handlers for a try block are tried in order of appearance. That makes it possible to write handlers that can never be executed, for example by placing a handler for a derived class after a handler for a corresponding base class.
Which is really what this warning is all about.

The example you give doesn't really demonstrate slicing, it's simply warning you that since B is-a A, the catch(A) effectively hides the catch(B).
To see the effects of slicing, you would have to do something with the A in the catch:
catch(A a) {
// Will always print class A, even when B is thrown.
std::cout << typeid(a).name() << std::endl;
}

Where does it say about hidden copy constructor in the base class?
The standard says nothing about a "hidden copy constructor." It does say stuff about an implicitly-defined copy constructor.
If you must have a quote:
If the class definition does not explicitly declare a copy constructor, there is no user-declared move constructor, one is declared implicitly.
In C++11, this can be declared default or delete, depending on the contents of the class in question. I'm not going to copy and paste the full list of reasons why a class might not be copyable. But suffice it to say, your class will get a default copy constructor.
Where does it say about this behaviour?
About what behavior? The behavior you see is exactly what you would expect to see in the following case:
void foo(A) {}
void main() {
foo(B());
}
You get slicing, because the parameter is taken by value. Which requires a copy.
Exception handlers are not like function calls; C++ will not choose the closest match. It will choose the first valid match. And since B is an A, it therefore matches catch(A).
Again, if you need some kind of quote (though I don't know why; any basic C++ book that describes exception handling will tell you this):
The handlers for a try block are tried in order of appearance. That makes it possible to write handlers that can never be executed, for example by placing a handler for a derived class after a handler for a corresponding base class.
Note that they even give an example that is exactly your example.
And I am aware that the exceptions should be handled by const reference.
And this is why you take exceptions by reference.

Related

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.)

c++ Inheriting private copy constructor: how doesn't this yield a compile time error?

In C++, if we have this class
class Uncopyable{
public:
Uncopyable(){}
~Uncopyable(){}
private:
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
};
and then we have a derived class
class Dervied: private Uncopyable{
};
My question is: why won't this generate a compile time error when the compiler generates the default copy constructor and assignment operators in the derived class ? Won't the generated code try to access base class private members ?
C++11 12.8/7 states "If the class definition does not explicitly declare a copy constructor, one is declared implicitly." so Dervied has an implicitly declared copy constructor. 12.8/11 says:
An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (8.4.3) if X has:
a variant member with a non-trivial corresponding constructor and X is a union-like class,
a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,
for the copy constructor, a non-static data member of rvalue reference type, or
for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.
Specifically, the third bullet applies: Dervied has a direct base class Uncopyable that cannot be copied because overload resolution results in a function that is inaccessible from Dervied::Dervied(const Dervied&). As a result Dervied's implicitly declared copy constructor is declared as deleted, resulting in a compile time error if and only if that copy constructor is called.
why won't this generate a compile time error when the compiler generates the default copy constructor and assignment operators in the derived class ?
Because the compiler generates them only when they are needed by the code being compiled. Write some code using the derived class where the copy constructor and/or assignment operator are involved, and you will see the compile-time error you are looking for.
The private in the inheritance makes them private to Derived, it can still see them, classes that use Derived can't.
The derived class will inherit the private copy constructor but will not need to use it unless you copy an object of derived type, as in this example.
The compiler does not auto-generate constructors/operators unless they are used and no other constructor/operator can be used to do that operation (i.e. a copy operation can be used in some situations where a move operation would suffice). The latter statement results in the following set of rules.
Here are the rules to the auto-generation of certain member functions:
Default constructor (if no other constructor is explicitly declared)
Copy constructor if no move constructor or move assignment operator
is explicitly declared. If a destructor is declared generation of a
copy constructor is deprecated.
Move constructor if no copy
constructor, move assignment operator or destructor is explicitly
declared.
Copy assignment operator if no move constructor or move assignment
operator is explicitly declared. If a destructor is declared
generation of a copy assignment operator is deprecated.
Move assignment operator if no copy constructor, copy assignment operator
or destructor is explicitly declared.
Destructor
The list is taken from this Wikipedia page.
One class cannot call private methods on another class, but it can inherit as much as it is coded too. This code just includes the member functions from Uncopyable in Derived.
Imagine if you wrote a class inheriting from std::vector. You can still erase, insert, push_back and do all those sorts of things. Because these are all public or protected vector member functions, they in turn call implementation specific private functions that do the low level things like manage memory. Your code in this derived class couldn't call those memory management functions directly though. This is used to insure the creators of the vector can change the internal details freely without breaking your use of the class.
If your example is what the code actually looks like, then this it is a common pattern used to make things that cannot be copied. It would make code like the following produce a compiler error:
Derived Foo;
Derived Bar;
Foo = Bar
It would also make the code throw an error on the following:
int GetAnswer(Derived bar)
{ /* Do something with a Derived */ }
Derived Foo;
int answer = GetAnser(Foo);
This example fails because a copy of foo is made and passed as the parameter in the function GetAnswer.
There are many reasons why something might not be copyable. The most common I have experienced is that the object manages some low level resource a single file, an opengl context, an audio output, etc... Imagine if you had a class that managed a log file. If it closed the file in the deconstructor, what happens to the original when a copy is destroyed.
Edit: to pass an Uncopyable class to a function, pass it by reference. The Following function does not make a copy:
int GetAnswer(Derived& bar)
{ /* Do something with a Derived */ }
Derived Foo;
int answer = GetAnser(Foo);
It would also cause a compiler error if all the constructor were private and the class was instantiated. But even if all the member function even constructors were private and the class was never instantiated that would be valid compiling code.
Edit: The reason a class with constructor is that there maybe other way to construct it or it maybe have static member functions, or class functions.
Sometimes factories are used to build object which have no obvious constructor. These might have functions to whatever magic is required to make the umakeable class instance. The most common I have seen is just that there was another constructor that was public, but not in an obvious place. I have also seen factories as friend classes to the unconstructable class so they could call the constructors and I have seen code manually twiddle bits of memory and cast pointers to the memory it to an instance of a class. All of these patterns are used to insure that a class is correctly created beyond just the guarantees C++ supplies.
A more common pattern I have seen is static member functions in classes.
class uncreateable
{
uncreateable() {}
public:
static int GetImportantAnswer();
};
Looking at this it can be seen that not only do I not need to create a instance of the class to call GetImportantAnswer() but I couldn't create an instance if I wanted. I could call this code using the following:
int answer;
answer = uncreateable::GetImportantAnswer();
Edit: Spelling and grammar
Well, actually this program does not compile with g++:
#include <iostream>
using namespace std;
class Uncopyable{
public:
Uncopyable(){}
~Uncopyable(){}
private:
Uncopyable(const Uncopyable&) {cout<<"in parent copy constructor";}
Uncopyable& operator=(const Uncopyable&) { cout << "in parent assignment operator";}
};
class Derived: private Uncopyable{
};
int main() {
Derived a;
Derived b = a;
}
compiler output:
$ g++ 23183322.cpp
23183322.cpp:10:88: warning: control reaches end of non-void function [-Wreturn-type]
Uncopyable& operator=(const Uncopyable&) { cout << "in parent assignment operator";}
^
23183322.cpp:13:7: error: base class 'Uncopyable' has private copy constructor
class Derived: private Uncopyable{
^
23183322.cpp:9:5: note: declared private here
Uncopyable(const Uncopyable&) {cout<<"in parent copy constructor";}
^
23183322.cpp:19:15: note: implicit copy constructor for 'Derived' first required here
Derived b = a;
^
1 warning and 1 error generated.

c++ syntax: default and delete modifiers

Today I stumbled over a code snippet like this one:
class A
{
A() = default;
A (const A&) = delete;
...
}
I've never seen either the delete or default keyword. Are they part of C++11 std? And what are they used for?
Special member functions can now be defaulted or deleted.
A deleted member function still takes part in overload resolution, but if it gets chosen, the program is ill-formed and compilation stops with a useful diagnostic. This is The Right Way to write things like non-copyable classes, and the user gets a proper error message.
A defaulted member function "does what it should", e.g. a defaulted default constructor default-initializes all bases and members and has empty body; a defaulted copy constructor copies each base and member object, and a defaulted assignment operator assigns each base and member object. If any of those operations aren't allowed (e.g. you have reference members), then the defaulted member function is defined as deleted.
Note that your first declaration-definition A() = default; makes the constructor A::A() user-declared but not user-defined; this is important for the classification of A, e.g. whether it is POD. (And notice that this is different from struct A { A(); }; A::A() = default; which is user-defined.)
Another nice consequence is the clarification of implicitly generated things: If you don't write certain functions yourself at all (like copy constructors), one gets implicitly declared for you. When the implicitly-declared one is odr-used, it gets implicitly defined as defaulted, and thus if it's not possible (e.g. if the class has non-copyable members), it actually gets implicitly defined as deleted. So that's generally a neat way of propagating things like non-copyability and non-assignability, at least in terms of the language and the consequent diagnostics.

Is it true that a default constructor is synthesized for every class that does not define one?

If the class doesn't have the constructor, will the compiler make one default constructor for it ?
Programmers new to C++ often have two common misunderstandings:
That a default constructor is synthesized for every class that does
not define one
from the book Inside the C++ Object Model
I am at a loss...
This is well explained in the section from which this quote is taken. I will not paraphrase it in its entirety, but here is a short summary of the section content.
First of all, you need to understand the following terms: implicitly-declared, implicitly-defined, trivial, non-trivial and synthesized (a term that is used by Stanley Lippman, but is not used in the standard).
implicitly-declared
A constructor is implicitly-declared for a class if there is no user-declared constructor in this class. For example, this class struct T { }; does not declare any constructor, so the compiler implicitly declares a default constructor. On the other hand, this class struct T { T(int); }; declares a constructor, so the compiler will not declare an implicit default constructor. You will not be able to create an instance of T without parameters, unless you define your own default constructor.
implicitly-defined
An implicitly-declared constructor is implicitly-defined when it is used, i.e. when an instance is created without parameters. Assuming the following class struct T { };, the line T t; will trigger the definition of T::T(). Otherwise, you would have a linker error since the constructor would be declared but not defined. However, an implicitly-defined constructor does not necessarily have any code associated with it! A default constructor is synthesized (meaning that some code is created for it) by the compiler only under certain circumstances.
trivial constructor
An implicitly-declared default constructor is trivial when:
its class has no virtual functions and no virtual base classes and
its base classes have trivial constructors and
all its non-static members have trivial constructors.
In this case, the default compiler has nothing to do, so there is no code synthesized for it. For instance, in the following code
struct Trivial
{
int i;
char * pc;
};
int main()
{
Trivial t;
}
the construction of t does not involve any operations (you can see that by looking at the generated assembly: no constructor is called to construct t).
non-trivial
On the other hand, if the class does not meet the three requirements stated above, its implicitly-declared default constructor will be non-trivial, meaning that it will involve some operations that must be performed in order to respect the language semantics. In this case, the compiler will synthesize an implementation of the constructor performing these operations.
For instance, consider the following class:
struct NonTrivial
{
virtual void foo();
};
Since it has a virtual member function, its default constructor must set the virtual table pointer to the correct value (assuming the implementation use a virtual method table, of course).
Similarly, the constructor of this class
struct NonTrivial
{
std::string s;
};
must call the string default constructor, as it is not trivial. To perform these operations, the compiler generates the code for the default constructor, and calls it anytime you create an instance without parameters. You can check this by looking at the assembly corresponding to this instantiation NonTrivial n; (you should see a function call, unless the constructor has been inlined).
Summary
When you don't provide any constructor for your class, the compiler implicitly declares a default one. If you try to use it, the compiler implicitly defines it, if it can (it is not always possible, for instance when a class has a non-default-constructible member). However, this implicit definition does not imply the generation of any code. The compiler needs to generate code for the constructor (synthesize it) only if it is non-trivial, meaning that it involves certain operations needed to implement the language semantics.
N.B.
Stanley B Lippman's "Inside the C++ object model" and this answer deals with (a possible) implementation of C++, not its semantics. As a consequence, none of the above can be generalized to all compilers: as far as I know, an implementation is perfectly allowed to generate code even for a trivial constructor. From the C++ user point of view, all that matters is the "implicitly-declared/defined` aspect (and also the trivial/non-trivial distinction, as it has some implications (for instance, an object of a class with non-trivial constructor cannot be a member of a union)).
I think the misconception is:
That a default constructor is synthesized for every class that does not define one
That people think the default constructor, which accepts no arguments, will always be generated if you don't declare it yourself.
However, this is not true, because if you declare any constructor yourself, the default one will not be automatically created.
class MyClass {
public:
MyClass(int x) {}; // No default constructor will be generated now
};
This will lead to problems like when beginners expect to use MyClass like this:
MyClass mc;
Which won't work because there is no default constructor that accepts no args.
edit as OP is still a little confused.
Imagine that my MyClass above was this:
class MyClass {
};
int main() {
MyClass m;
}
That would compile, because the compiler will autogenerate the default constructor MyClass() because MyClass was used.
Now take a look at this:
#include <iostream>
class MyClass {
};
int main() {
std::cout << "exiting\n";
}
If this were the only code around, the compiler wouldn't even bother generating the default constructor, because MyClass is never used.
Now this:
#include <iostream>
class MyClass {
public:
MyClass(int x = 5) { _x = x; }
int _x;
};
int main() {
MyClass m;
std::cout << m._x;
}
The compiler doesn't generate default constructor MyClass(), because the class already has a constructor defined by me. This will work, and MyClass(int x = 5) works as your default constructor because it can accept no arguments, but it wasn't generated by the compiler.
And finally, where beginners might run into a problem:
class MyClass() {
public:
MyClass(int x) { _x = x; }
int _x;
};
int main() {
MyClass m;
}
The above will throw you an error during compilation, because MyClass m needs a default constructor (no arguments) to work, but you already declared a constructor that takes an int. The compiler will not generate a no-argument constructor in this situation either.
A default constructor is synthesized for every class that does not define one if:
The code using the class needs one & only if
There is no other constructor explicitly defined for the class by you.
All the upvoted answers thus far seem to say approximately the same thing:
A default constructor is synthesized for every class that does not have any user-defined constructor.
which is a modification of the statement in the question, which means
A default constructor is synthesized for every class that does not have a user-defined default constructor.
The difference is important, but the statement is still wrong.
A correct statement would be:
A default constructor is synthesized for every class that does not have any user-defined constructor and for which all sub-objects are default-constructible in the context of the class.
Here are some clear counter-examples to the first statement:
struct NoDefaultConstructor
{
NoDefaultConstructor(int);
};
class Surprise1
{
NoDefaultConstructor m;
} s1; // fails, no default constructor exists for Surprise1
class Surprise1 has no user-defined constructors, but no default constructor is synthesized.
It doesn't matter whether the subobject is a member or a base:
class Surprise2 : public NoDefaultConstructor
{
} s2; // fails, no default constructor exists for Surprise2
Even if all subobjects are default-constructible, the default constructor has to be accessible from the composite class:
class NonPublicConstructor
{
protected:
NonPublicConstructor();
};
class Surprise3
{
NonPublicConstructor m;
} s3; // fails, no default constructor exists for Surprise3
Yes a default constructor is always there by default if you don't define a constructor of your own (see the default constructor section here).
http://www.codeguru.com/forum/archive/index.php/t-257648.html
Quote:
The following sentense are got from the book "Inside the C++ object model" , written by Stanley B. Lippman.
There are four characteristics of a class under which the compiler
needs to synthesize a default constructor for classes that declare no
constructor at all. The Standard refers to these as implicit,
nontrivial default constructors. The synthesized constructor fulfills
only an implementation need. It does this by invoking member object or
base class default constructors or initializing the virtual function
or virtual base class mechanism for each object. Classes that do not
exhibit these characteristics and that declare no constructor at all
are said to have implicit, trivial default constructors. In practice,
these trivial default constructors are not synthesized. ...
Programmers new to C++ often have two common misunderstandings:
That a default constructor is synthesized for every class that does
not define one
That the compiler-synthesized default constructor provides explicit
default initializers for each data member declared within the class
As you have seen, neither of these is true.

Why must a base class destructor be accessible only when a custom constructor is declared?

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.