Related
There is already a question asking about the "real-world" behavior of deleteing a pointer to a base class that lacks a virtual destructor, but the question is restricted to a very limited case (the derived class has no members with non-trivial destructors), and the accepted answer just says there's no way to know without checking the behavior of every compiler.
....but that isn't actually very helpful; knowing that every compiler might behave differently doesn't tell us anything about the behavior of any particular compiler. So, what do Clang and G++ do in this case? I would assume they would simply call the base-class destructor, then deallocate the memory (for the entire derived class). Is this the case?
Or, if it's not possible to determine this for all versions of GCC and Clang, how about GCC 4.9 and 5.1, and Clang 3.5 through 3.7?
First, the standard disclaimer: this is undefined behavior, so even with one specific compiler, changing the compiler flags, the day of the week, or the way you look at the computer could change the behavior.
The following all assumes you have some sort of at least slightly non-trivial destruction happening in your destructors (e.g., the objects delete some memory, or contain object others that themselves delete some memory).
In the simple case (single inheritance) you typically get something roughly equivalent to static binding--that is, if you destroy a derived object via a pointer to a base object, only the base constructor is invoked so the object isn't destroyed properly.
If you use multiple inheritance, and you destroy an object of derived class via the "first" base class, it'll typically be about the same as if you used single inheritance--the base class destructor will be invoked, but the derived class destructor won't be.
If you have multiple inheritance and destroy a derived object via a pointer to the second (or subsequent) base class, your program will typically crash. With multiple inheritance, you have multiple base class objects at multiple offsets in the derived object.
In the typical case, the first base class will be at the beginning of the derived object, so using the address of derived as a pointer to the first base class object works about the same as in the single inheritance case--we get the equivalent of static binding/static dispatch.
If we try this with any of the other base classes, a pointer to the derived doesn't point to an object of that base class. The pointer needs to be adjusted to point to the second (or subsequent) base class before it can be used as a pointer to that type of object at all.
With a non-virtual destructor, what'll typically happen is that the code will basically take that address of that first base class object, do roughly the equivalent of a reinterpret_cast on it, and try to use that memory as if it were an object of the base class specified by the pointer (e.g., base2). For example, let's assume base2 has a pointer at offset 14, and base2's destructor attempts to delete a block of memory it points at. With a non-virtual destructor, it'll probably receive a pointer to the base1 subject--but it'll still look at offset 14 from there, and try to treat that as a pointer, and pass it to delete. It could be that base1 contains a pointer at that offset, and it's actually pointing at some dynamically allocated memory, in which case this might actually appear to succeed. Then again, it could also be that it's something entirely different, and the program dies with an error message about (for example) attempting to free an invalid pointer.
It's also possible that base1 is smaller that 14 bytes in size, so this ends up actually manipulating (say) offset 4 in base2.
Bottom line: for a case like this, things get really ugly in a hurry. The very best you can hope for is that the program dies quickly and loudly.
Just for kicks, quick demo code:
#include <iostream>
#include <string>
#include <vector>
class base{
char *data;
std::string s;
std::vector<int> v;
public:
base() { data = new char; v.push_back(1); s.push_back('a'); }
~base() { std::cout << "~base\n"; delete data; }
};
class base2 {
char *data2;
public:
base2() : data2(new char) {}
~base2() { std::cout << "~base2\n"; delete data2; }
};
class derived : public base, public base2 {
char *more_data;
public:
derived() : more_data(new char) {}
~derived() { std::cout << "~derived\n"; delete more_data; }
};
int main() {
base2 *b = new derived;
delete b;
}
g++/Linux: Segmentation fault
clang/Linux: Segmentation fault
VC++/Windows: Popup: "foo.exe has stopped working" "A problem caused the program to stop working correctly. Please close the program."
If we change the pointer to base instead of base2, we get ~base from all the compilers (and if we derive only from one base class, and use a pointer to that base class, we get the same: only that base class' destructor runs).
If you delete an object without a virtual destructor, the compiler will probably assume that the deleted address is the address of the most derived object.
Unless you use a primary base class to delete the object, this won't be the case, so the compiler will call operator delete with an incorrect address.
Of course the compiler will not call the destructor of the derived class, or operator delete of the derived class (if there is one).
We use a framework that relies on memcpy in certain functions. To my understanding I can give everything that is trivially copyable into these functions.
Now we want to use a simple class hierarchy. We are not sure whether we can have a class hierarchy that results in trivially copyable types because of the safe destruction. The example code looks like this.
class Timestamp; //...
class Header
{
public:
uint8_t Version() const;
const Timestamp& StartTime();
// ... more simple setters and getters with error checking
private:
uint8_t m_Version;
Timestamp m_StartTime;
};
class CanData : public Header
{
public:
uint8_t Channel();
// ... more setters and getters with error checking
private:
uint8_t m_Channel;
};
The base class is used in several similar subclasses. Here I omitted all constructors and destructors. Thus the classes are trivially copyable. I suppose though that the user can write a code that results in a memory leak like this:
void f()
{
Header* h = new CanData();
delete h;
}
Is it right that the class hierarchy without the virtual destructor is a problem even if all classes use the compiler's default destructor? Is it therefore right that I cannot have a safe class hierarchy that is trivially copyable?
This code
Header* h = new CanData();
delete h;
will trigger undefined behavior since §5.3.5/p3 states:
In the first alternative (delete object), if the static type of the object to be deleted is different from its
dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the
static type shall have a virtual destructor or the behavior is undefined
and regardless of not having dynamically allocated objects contained in your derived class (really bad if you have), you shouldn't do it. Having a class hierarchy without the base class virtual destructor is not a problem per se, it becomes a problem when you try to mix static and dynamic types with delete.
Doing memcpy on a derived class object smells of bad design to me, I would rather address the need for a "virtual constructor" (i.e. a virtual clone() function in your base class) to duplicate your derived objects.
You can have your class hierarchy that is trivially copyable if you make sure that your object, its subobjects and base classes are trivially copyable. If you want to prevent users referring to your derived objects via base classes you could, as Mark first suggested, render the inheritance protected
class Header
{
public:
};
class CanData : protected Header
{ ^^^^^^^^^
public:
};
int main() {
Header *pt = new CanData(); // <- not allowed
delete pt;
}
Notice that you won't be able to use base pointers at all to refer to derived objects due to §4.10/p3 - pointer conversions.
If you delete a pointer to a derived type held as its base type and you don't have a virtual destructor, the derived types destructor won't be called, whether it's implicitly generated or not. And whether its implicitly generated or not, you want it to be called. If the derived type's destructor wouldn't actually do anything anyway though, it might not leak anything or cause a problem. If the derived type holds something like a std::string, std::vector, or anything with a dynamic allocation, you want the dtors to be called. As a matter of good practice, you always want a virtual destructor for base classes whether or not the derived classes destructors need to be called (since a base class shouldn't know about what derives from it, it shouldn't make an assumption like this).
If you copy a type like so:
Base* b1 = new Derived;
Base b2 = *b1;
You will only invoke Bases copy ctor. The parts of the object which are actually from Derived will not be involved. b2 will not secretly be a Derived, it will just be a Base.
My first instinct is "don't do that - find another way, a different framework, or fix the framework". But just for fun let's assume that for certain your class copy doesn't depend in any way on the copy constructor of the class or any of its comprised parts being called.
Then since you're clearly inheriting to implement rather than to substitute the solution is easy: Use protected inheritance and your problem is solved, because they can no longer polymorphically access or delete your object, preventing the undefined behavior.
It's almost safe. In particular, there is no memory leak in
Header* h = new CanData();
delete h;
delete h calls the destructor of Header and then frees the memory pointed to by h. The amount of memory freed is the same as was initially allocated at that memory address, not the sizeof(Header). Since Header and CanData are trivial, their destructors do nothing.
However, you must provide a virtual destructor to base even if it does nothing (by requirement of the standard to avoid undefined behaviour). A common guideline is that a destructor for a base class must be either public and virtual or protected and nonvirtual
Of course, you must beware slicing as usual.
Thanks all for posting various suggestions. I try a summarizing answer with an additional proposal for the solution.
The prerequisite of my question was to reach a class hierarchy that is trivially copyable. See http://en.cppreference.com/w/cpp/concept/TriviallyCopyable and especially the requirement of a trivial destructor (http://en.cppreference.com/w/cpp/language/destructor#Trivial_destructor). The class cannot need a destructor implemented. This restricts the allowed data members, but is fine for me. The example shows only C-compatible types without dynamic memory allocation.
Some pointed out that the problem of my code is undefined behaviour, not necessarily a memory leak. Marco quoted the standard regarding this. Thanks, really helpful.
From my understanding of the answers, possible solutions are the following. Please correct me if I am wrong. The solution's point is that the implementation of the base class must avoid that its destructor can be called.
Solution 1: The proposed solutions use protected inheritance.
class CanData : protected Header
{
...
};
It works but avoids that people can access the public interface of Header. This was the original intention to have a base class. CanData needs to forward these functions to Header. In the consequece, I would reconsider to use composition instead of inheritance here. But the solution should work.
Solution 2: Header's destructor must be protected, not the base class as a whole.
class Header
{
public:
uint8_t Version() const;
const Timestamp& StartTime();
// ... more simple setters and getters with error checking
protected:
~Header() = default;
private:
uint8_t m_Version;
Timestamp m_StartTime;
};
Then no user can delete Header. This is fine for me, because Header has no purpose on its own. With public derivation, the public interface remains available to the user.
My understanding is that CanData needs not implement a destructor to call the base class's desctructor. All can use the default destructor. I am not completely sure about this though.
All in all, the answers to my questions in the end of the origial positing are:
Is it right that the class hierarchy without the virtual destructor is a problem even if all classes use the compiler's default destructor?
It is only a problem if your destructor is public. You must avoid that people can access you desctrutor, except for derived classes. And you must ensure that derived classes call (implicitely) the base class's destructor.
Is it therefore right that I cannot have a safe class hierarchy that is trivially copyable?
You can make your base class safe with protected inheritance or a protected desctructor. Then you can have a hierarchy of trivially copyable classes.
Consider the following code:
#include <iostream>
#include <type_traits>
// Abstract base class
template<class Crtp>
class Base
{
// Lifecycle
public: // MARKER 1
Base(const int x) : _x(x) {}
protected: // MARKER 2
~Base() {}
// Functions
public:
int get() {return _x;}
Crtp& set(const int x) {_x = x; return static_cast<Crtp&>(*this);}
// Data members
protected:
int _x;
};
// Derived class
class Derived
: public Base<Derived>
{
// Lifecycle
public:
Derived(const int x) : Base<Derived>(x) {}
~Derived() {}
};
// Main
int main()
{
Derived d(5);
std::cout<<d.set(42).get()<<std::endl;
return 0;
}
If I want a public inheritance of Derived from Base, and if I don't want a virtual destructor in the base class, what would be the best keywords for the constructor (MARKER 1) and the destructor (MARKER 2) of Base to guarantee that nothing bad can happen ?
Whatever programming style you use, you can alwyas do something bad: even if you follow the best of the bestest guideline practice. That's something physical behind it (and relate to the impossibility to reduce the global entrophy)
That said, don't confuse "classic OOP" (a methodology) with C++ (a language), OOP inheritache (a relation) with C++ inheritance (an aggregation mechanism) and OOP polymorphism (a model) with C++ runtime and static polymorphism (a dispatching mechanism).
Although names sometime matches, the C++-things don't have to necessarily sevicing OOP-things.
Public inheritance from a base with some non-virtual methods is normal. and destructor is not special: just dont call delete on the CRTP base.
Unlike with classic OOP, a CRTP-base has different type for each of the deriveds, so having a "pointer to a base" is clueless since there is no "pointer to a common type". And hence the risk to call "delete pbase" is very limited.
The "protected-dtor paradigm" is valid only if you are programming OOP-inheritance using C++ inheritance for object managed (and deleted) though pointer-based polymorphism. If you are following other paradigms, those rules should not be treated in a literal way.
In your case, the proteced-dtor just deny you to create a Base<Derived> on the stack and to call delete on a Base*. Something you will never do, since Base with no "Dervied" has no sense to exist, and having a Base<Derived>* makes no sense since you can have just a Derived*, hence having both public ctor and dtor makes no particular mess.
But you can even do the opposite choice to have both ctor and dtor protected, since you will never construct a Base alone, since it always needs a Derived type to be known.
Because of the particular construction of CRTP, all the classical OOP stuff leads to a sort of "indifferent equilibrium", since there is no more the "dangerous usecase".
You can use them or not, but no particular bad-thing can happen. Not if you use object the way they had been designed to be used.
While your code works I find it odd to mark the destructor rather than the constructor as protected. Normally my reasoning would be that you want to prevent the programmer from accidentally creating a CRTP base object. It all comes down to the same of course, but this is hardly canonical code.
The only thing that your code prevents is the accidental deletion of a CRTP object via a base pointer – i.e. a case like this:
Base<Derived>* base = new Derived;
delete base;
But that is a highly artificial situation that won’t arise in real code since CRTP simply isn’t supposed to be used that way. The CRTP base is an implementation detail that should be completely hidden from the client code.
So my recipe for this situation would be:
Define a protected constructor.
Don’t define a destructor – or, if required for the CRTP semantic, define it as public (and non-virtual).
There's no problem, since the destructor is protected it means that client code can't delete a pointer to Base, so there's no problem with Base's destructor being non-virtual.
In Why is there no base class in C++?, I quoted Stroustrup on why a common Object class for all classes is problematic in c++. In that quote there is the statement:
Using a universal base class implies cost: Objects must be heap-allocated to be polymorphic;
I really didn't look twice at it, and since its on Bjarnes home page I would suppose a lot of eyes have scanned that sentence and reported any misstatements.
A commenter however pointed out that this is probably not the case, and in retrospect I can't find any good reason why this should be true. A short test case yields the expected result of VDerived::f().
struct VBase {
virtual void f() { std::cout <<"VBase::f()\n"; }
};
struct VDerived: VBase {
void f() { std::cout << "VDerived::f()\n"; }
};
void test(VBase& obj) {
obj.f();
}
int main() {
VDerived obj;
test(obj);
}
Of course if the formal argument to test was test(VBase obj) the case would be totally different, but that would not be a stack vs. heap argument but rather copy semantics.
Is Bjarne flat out wrong or am I missing something here?
Addendum:
I should point out that Bjarne has added to the original FAQ that
Yes. I have simplified the arguments; this is an FAQ, not an academic paper.
I understand and sympathize with Bjarnes point. Also I suppose my eyes was one of the pairs scanning that sentence.
Looks like polymorphism to me.
Polymorphism in C++ works when you have indirection; that is, either a pointer-to-T or a reference-to-T. Where T is stored is completely irrelevant.
Bjarne also makes the mistake of saying "heap-allocated" which is technically inaccurate.
(Note: this doesn't mean that a universal base class is "good"!)
I think Bjarne means that obj, or more precisely the object it points to, can't easily be stack-based in this code:
int f(int arg)
{
std::unique_ptr<Base> obj;
switch (arg)
{
case 1: obj = std::make_unique<Derived1 >(); break;
case 2: obj = std::make_unique<Derived2 >(); break;
default: obj = std::make_unique<DerivedDefault>(); break;
}
return obj->GetValue();
}
You can't have an object on the stack which changes its class, or is initially unsure what exact class it belongs to.
(Of course, to be really pedantic, one could allocate the object on the stack by using placement-new on an alloca-allocated space. The fact that there are complicated workarounds is beside the point here, though.)
The following code also doesn't work as might be expected:
int f(int arg)
{
Base obj = DerivedFactory(arg); // copy (return by value)
return obj.GetValue();
}
This code contains an object slicing error: The stack space for obj is only as large as an instance of class Base; when DerivedFactory returns an object of a derived class which has some additional members, they will not be copied into obj which renders obj invalid and unusable as a derived object (and quite possibly even unusable as a base object.)
Summing up, there is a class of polymorphic behaviour that cannot be achieved with stack objects in any straightforward way.
Of course any completely constructed derived object, wherever it is stored, can act as a base object, and therefore act polymorphically. This simply follows from the is-a relationship that objects of inherited classes have with their base class.
Having read it I think the point is (especially given the second sentence about copy-semantics) that universal base class is useless for objects handled by value, so it would naturally lead to more handling via reference and thus more memory allocation overhead (think template vector vs. vector of pointers).
So I think he meant that the objects would have to be allocated separately from any structure containing them and that it would have lead to many more allocations on heap. As written, the statement is indeed false.
PS (ad Captain Giraffe's comment): It would indeed be useless to have function
f(object o)
which means that generic function would have to be
f(object &o)
And that would mean the object would have to be polymorphic which in turn means it would have to be allocated separately, which would often mean on heap, though it can be on stack. On the other hand now you have:
template <typename T>
f(T o) // see, no reference
which ends up being more efficient for most cases. This is especially the case of collections, where if all you had was a vector of such base objects (as Java does), you'd have to allocate all the objects separately. Which would be big overhead especially given the poor allocator performance at time C++ was created (Java still has advantage in this because copying garbage collector are more efficient and C++ can't use one).
Bjarne's statement is not correct.
Objects, that is instances of a class, become potentially polymorphic by adding at least one virtual method to their class declaration. Virtual methods add one level of indirection, allowing a call to be redirected to the actual implementation which might not be known to the caller.
For this it does not matter whether the instance is heap- or stack-allocated, as long as it is accessed through a reference or pointer (T& instance or T* instance).
One possible reason why this general assertion slipped onto Bjarne's web page might be that it is nonetheless extremely common to heap-allocate instances with polymorphic behavior. This is mainly because the actual implementation is indeed not known to the caller who obtained it through a factory function of some sort.
I think he was going along the lines of not being able to store it in a base-typed variable. You're right in saying that you can store it on the stack if it's of the derived type because there's nothing special about that; conceptually, it's just storing the data of the class and it's derivatives + a vtable.
edit: Okay, now I'm confused, re-looking at the example. It looks like you may be right now...
I think the point is that this is not "really" polymorphic (whatever that means :-).
You could write your test function like this
template<class T>
void test(T& obj)
{
obj.f();
}
and it would still work, whether the classes have virtual functions or not.
Polymorphism without heap allocation is not only possible but also relevant and useful in some real life cases.
This is quite an old question with already many good answers. Most answers indicate, correctly of course, that Polymorphism can be achieved without heap allocation. Some answers try to explain that in most relevant usages Polymorphism needs heap allocation.
However, an example of a viable usage of Polymorphism without heap allocation seems to be required (i.e. not just purely syntax examples showing it to be merely possible).
Here is a simple Strategy-Pattern example using Polymorphism without heap allocation:
Strategies Hierarchy
class StrategyBase {
public:
virtual ~StrategyBase() {}
virtual void doSomething() const = 0;
};
class Strategy1 : public StrategyBase {
public:
void doSomething() const override { std::cout << "Strategy1" << std::endl; }
};
class Strategy2 : public StrategyBase {
public:
void doSomething() const override { std::cout << "Strategy2" << std::endl; }
};
A non-polymorphic type, holding inner polymorphic strategy
class A {
const StrategyBase* strategy;
public:
// just for the example, could be implemented in other ways
const static Strategy1 Strategy_1;
const static Strategy2 Strategy_2;
A(const StrategyBase& s): strategy(&s) {}
void doSomething() const { strategy->doSomething(); }
};
const Strategy1 A::Strategy_1 {};
const Strategy2 A::Strategy_2 {};
Usage Example
int main() {
// vector of non-polymorphic types, holding inner polymorphic strategy
std::vector<A> vec { A::Strategy_1, A::Strategy_2 };
// may also add strategy created on stack
// using unnamed struct just for the example
struct : StrategyBase {
void doSomething() const override {
std::cout << "Strategy3" << std::endl;
}
} strategy3;
vec.push_back(strategy3);
for(auto a: vec) {
a.doSomething();
}
}
Output:
Strategy1
Strategy2
Strategy3
Code: http://coliru.stacked-crooked.com/a/21527e4a27d316b0
Let's assume we have 2 classes
class Base
{
public:
int x = 1;
};
class Derived
: public Base
{
public:
int y = 5;
};
int main()
{
Base o = Derived{ 50, 50 };
std::cout << Derived{ o }.y;
return 0;
}
The output will be 5 and not 50. The y is cut off. If the member variables and the virtual functions are the same, there is the illusion that polymorphism works on the stack as a different VTable is used. The example below illustrates that the copy constructor is called. The variable x is copied in the derived class, but the y is set by the initialization list of a temporary object.
The stack pointer has increased by 4 as the class Base holds an integer. The y will just be cut off in the assignment.
When using Polymorphism on the heap you tell the new allocator which type you allocate and by that how much memory on heap you need. With the stack this does not work. And neither memory is shrinking or increasing on the heap. As at the time of initialization you know what you're initializing and exact this amount of memory is allocated.
Just what the topic asks. Also want to know why non of the usual examples of CRTP do not mention a virtual dtor.
EDIT:
Guys, Please post about the CRTP prob as well, thanks.
Only virtual functions require dynamic dispatch (and hence vtable lookups) and not even in all cases. If the compiler is able to determine at compile time what is the final overrider for a method call, it can elide performing the dispatch at runtime. User code can also disable the dynamic dispatch if it so desires:
struct base {
virtual void foo() const { std::cout << "base" << std::endl; }
void bar() const { std::cout << "bar" << std::endl; }
};
struct derived : base {
virtual void foo() const { std::cout << "derived" << std::endl; }
};
void test( base const & b ) {
b.foo(); // requires runtime dispatch, the type of the referred
// object is unknown at compile time.
b.base::foo();// runtime dispatch manually disabled: output will be "base"
b.bar(); // non-virtual, no runtime dispatch
}
int main() {
derived d;
d.foo(); // the type of the object is known, the compiler can substitute
// the call with d.derived::foo()
test( d );
}
On whether you should provide virtual destructors in all cases of inheritance, the answer is no, not necessarily. The virtual destructor is required only if code deletes objects of the derived type held through pointers to the base type. The common rule is that you should
provide a public virtual destructor or a protected non-virtual destructor
The second part of the rule ensures that user code cannot delete your object through a pointer to the base, and this implies that the destructor need not be virtual. The advantage is that if your class does not contain any virtual method, this will not change any of the properties of your class --the memory layout of the class changes when the first virtual method is added-- and you will save the vtable pointer in each instance. From the two reasons, the first being the important one.
struct base1 {};
struct base2 {
virtual ~base2() {}
};
struct base3 {
protected:
~base3() {}
};
typedef base1 base;
struct derived : base { int x; };
struct other { int y; };
int main() {
std::auto_ptr<derived> d( new derived() ); // ok: deleting at the right level
std::auto_ptr<base> b( new derived() ); // error: deleting through a base
// pointer with non-virtual destructor
}
The problem in the last line of main can be resolved in two different ways. If the typedef is changed to base1 then the destructor will correctly be dispatched to the derived object and the code will not cause undefined behavior. The cost is that derived now requires a virtual table and each instance requires a pointer. More importantly, derived is no longer layout compatible with other. The other solution is changing the typedef to base3, in which case the problem is solved by having the compiler yell at that line. The shortcoming is that you cannot delete through pointers to base, the advantage is that the compiler can statically ensure that there will be no undefined behavior.
In the particular case of the CRTP pattern (excuse the redundant pattern), most authors do not even care to make the destructor protected, as the intention is not to hold objects of the derived type by references to the base (templated) type. To be in the safe side, they should mark the destructor as protected, but that is rarely an issue.
Very unlikely indeed. There's nothing in the standard to stop compilers doing whole classes of stupidly inefficient things, but a non-virtual call is still a non-virtual call, regardless of whether the class has virtual functions too. It has to call the version of the function corresponding to the static type, not the dynamic type:
struct Foo {
void foo() { std::cout << "Foo\n"; }
virtual void virtfoo() { std::cout << "Foo\n"; }
};
struct Bar : public Foo {
void foo() { std::cout << "Bar\n"; }
void virtfoo() { std::cout << "Bar\n"; }
};
int main() {
Bar b;
Foo *pf = &b; // static type of *pf is Foo, dynamic type is Bar
pf->foo(); // MUST print "Foo"
pf->virtfoo(); // MUST print "Bar"
}
So there's absolutely no need for the implementation to put non-virtual functions in the vtable, and indeed in the vtable for Bar you'd need two different slots in this example for Foo::foo() and Bar::foo(). That means it would be a special-case use of the vtable even if the implementation wanted to do it. In practice it doesn't want to do it, it wouldn't make sense to do it, don't worry about it.
CRTP base classes really ought to have destructors that are non-virtual and protected.
A virtual destructor is required if the user of the class might take a pointer to the object, cast it to the base class pointer type, then delete it. A virtual destructor means this will work. A protected destructor in the base class stops them trying it (the delete won't compile since there's no accessible destructor). So either one of virtual or protected solves the problem of the user accidentally provoking undefined behavior.
See guideline #4 here, and note that "recently" in this article means nearly 10 years ago:
http://www.gotw.ca/publications/mill18.htm
No user will create a Base<Derived> object of their own, that isn't a Derived object, since that's not what the CRTP base class is for. They just don't need to be able to access the destructor - so you can leave it out of the public interface, or to save a line of code you can leave it public and rely on the user not doing something silly.
The reason it's undesirable for it to be virtual, given that it doesn't need to be, is just that there's no point giving a class virtual functions if it doesn't need them. Some day it might cost something, in terms of object size, code complexity or even (unlikely) speed, so it's a premature pessimization to make things virtual always. The preferred approach among the kind of C++ programmer who uses CRTP, is to be absolutely clear what classes are for, whether they are designed to be base classes at all, and if so whether they are designed to be used as polymorphic bases. CRTP base classes aren't.
The reason that the user has no business casting to the CRTP base class, even if it's public, is that it doesn't really provide a "better" interface. The CRTP base class depends on the derived class, so it's not as if you're switching to a more general interface if you cast Derived* to Base<Derived>*. No other class will ever have Base<Derived> as a base class, unless it also has Derived as a base class. It's just not useful as a polymorphic base, so don't make it one.
The answer to your first question: No. Only calls to virtual functions will cause an indirection via the virtual table at runtime.
The answer to your second question: The Curiously recurring template pattern is commonly implemented using private inheritance. You don't model an 'IS-A' relationship and hence you don't pass around pointers to the base class.
For instance, in
template <class Derived> class Base
{
};
class Derived : Base<Derived>
{
};
You don't have code which takes a Base<Derived>* and then goes on to call delete on it. So you never attempt to delete an object of a derived class through a pointer to the base class. Hence, the destructor doesn't need to be virtual.
Firstly, I think the answer to the OP's question has been answered quite well - that's a solid NO.
But, is it just me going insane or is something going seriously wrong in the community? I felt a bit scared to see so many people suggesting that it's useless/rare to hold a pointer/reference to Base. Some of the popular answers above suggest that we don't model IS-A relationship with CRTP, and I completely disagree with those opinions.
It's widely known that there's no such thing as interface in C++. So to write testable/mockable code, a lot of people use ABC as an "interface". For example, you have a function void MyFunc(Base* ptr) and you can use it this way: MyFunc(ptr_derived). This is the conventional way to model IS-A relationship which requires vtable lookups when you call any virtual functions in MyFunc. So this is pattern one to model IS-A relationship.
In some domain where performance is critical, there exists another way(pattern two) to model IS-A relationship in a testable/mockable manner - via CRTP. And really, performance boost can be impressive(600% in the article) in some cases, see this link. So MyFunc will look like this template<typename Derived> void MyFunc(Base<Derived> *ptr). When you use MyFunc, you do MyFunc(ptr_derived); The compiler is going to generate a copy of code for MyFunc() that matches best with the parameter type ptr_derived - MyFunc(Base<Derived> *ptr). Inside MyFunc, we may well assume some function defined by the interface is called, and pointers are statically cast-ed at compile time(check out the impl() function in the link), there's no overheads for vtable lookups.
Now, can someone please tell me either I am talking insane nonsense or the answers above simply did not consider the second pattern to model IS-A relationship with CRTP?