Reputation: 1970
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
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
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
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