Hot.PxL
Hot.PxL

Reputation: 1970

How to force use of curiously recurring template pattern in C++

I have the following base template class.

template<typename T>
class Base {
  public:
    void do_something() {
    }
};

It is intended to be used as a curiously recurring template pattern. It should be inherited like class B : public Base<B>. It must not be inherited like class B : public Base<SomeoneElse>. I want to statically enforce this requirement. If someone uses this wrong, I expect an error in the compiling phase.

What I'm doing is putting a static_cast<T const&>(*this) in do_something(). This way the class inheriting the template is or inherits from the class provided as the template parameter. Sorry for the confusing expression. In plain English, it requires B is or inherits from SomeoneElse in class B : public Base<SomeoneElse>.

I don't know if it's the optimal way to achieve this. Looks gross to me.

However I want to do more. I want to ensure B is SomeoneElse itself. How can I do that?

Upvotes: 8

Views: 1014

Answers (3)

Richard Hodges
Richard Hodges

Reputation: 69882

Two good answers so far. Here is another which uses the idiom of generating custom access keys to certain methods (in this case a constructor). It provides an absolute guarantee of correct use while not exposing private methods in the base to the derived.

It can also be used to control access to other methods in the base class on a case-by-case basis.

template<class Derived>
struct Base
{
private:
    // make constructor private
    Base() = default;
protected:
    // This key is protected - so visible only to derived classes
    class creation_key{
        // declare as friend to the derived class
        friend Derived;
        // make constructor private - only the Derived may create a key
        creation_key() = default;
    };

    // allow derived class to construct me with a key
    Base(creation_key)
    {}

    // other methods available to the derived class go here

private:
    // the rest of this class is private, even to the derived class
    // (good encapsulation)
};

struct D1 : Base<D1>
{
    // provide the key
    D1()
    : Base<D1>(creation_key())
    {}

};

Upvotes: 1

Daniel Earwicker
Daniel Earwicker

Reputation: 116674

If your class contains some code that says:

T* pT = 0;
Base *pB = pT;

Then there will be a compiler error if T is not assignment-compatible with Base.

This kind of check is formalised in C++11 so you don't have to write it by hand and can get helpful error messages:

#include <type_traits>

template<typename T>
class Base {

public:
    void do_something() 
    {
        static_assert(
            std::is_base_of<Base, T>::value,
            "T must be derived from Base");
    }
};

class B : public Base<B> { };

int main()
{
    B b;
    b.do_something();
}

As to ensuring that Base's type parameter is exactly the class that is deriving from it, that seems conceptually flawed. A class that is acting as a base class can't "talk about" the type that is inheriting it. It may be inherited more than once via multiple inheritance, or not at all.

Upvotes: 5

T.C.
T.C.

Reputation: 137330

Make the constructor (or destructor) of Base private, and then make T a friend. This way the only thing that can construct/destruct a Base<T> is a T.

Upvotes: 10

Related Questions