Search code examples
c++inheritancepolymorphismvirtual-functionscrtp

Can CRTP-based derived classes be safely deleted with a virtual destructor in the base class?


I have a c++ question about CRTP pattern and polymorphism. I want to know if I can put a virtual destructor in a CRTP pattern without causing some undefined behavior when implementing a derived class with CRTP.

I have this code for an abstract class:

struct TensorExpression {
    virtual ~TensorExpression() = default;
    // ...     
};

Then I have derived classes like BinaryOperator:

template<class Operation>
class BinaryOperator : public TensorExpression {
public:
    ~virtual BinaryOperator() override = default;
    void operation() {
        return static_cast<Operation*>(this)->operation_implementation();
    }
    // some other methods to be implemented with CRTP pattern called with static_cast.
};

Then I want to implement those classes with CRTP to avoid more virtual methods.

class BinaryAdd : public BinaryOperator<BinaryAdd> {
    public:
    ~BinaryAdd() override = default;
    //
    private:
    friend class public BinaryOperator<BinaryAdd>;
    void operation_implementation() {
        std::cout << "add implementation" ;
    }
    //
};

My question is, won't a virtual destructor in a CRTP base class cause undefined behavior? and is it necesary? or is there another whay to delete derived CRTP class using a TensorExpression pointer without using a virtual destructor in BinaryOperator? I mean, I want to use TensorExpression polimorphically, but not BinaryOperator; given that I won't be creating BinaryOperator pointers directly to be deleted. BinaryAdd just implements BinaryOperator.


Solution

  • [class.dtor]

    If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly-declared) is virtual.

    You don't need to specify the destructor of BinaryOperator. It will be declared virtual because TensorExpression's destructor is. Same for BinaryAdd.

    There is no reason CRTP would prevent dynamic polymorphism from working correctly. Deleting a BinaryAdd object through a TensorExpression pointer will work just fine, simply because the destructor of TensorExpression is virtual.

    Your example structure is even listed as a pattern in the wikipedia page for CRTP.