16

Using CRTP sometimes I write a code like this:

// this was written first
struct Foo : Base<Foo, ...>
{
   ...
};

// this was copy-pasted from Foo some days later
struct Bar : Base<Foo, ...>
{
   ...
};

And it's very difficult to understand what goes wrong, until I trace code in debugger and see that Bar's members aren't used in Base.

How to reveal this error at compile time?

(I use MSVC2010, so I can use some C++0x features and MSVC language extensions)

Abyx
  • 12,345
  • 5
  • 44
  • 76

5 Answers5

17

In C++0x you have a simple solution. I don't know whether it is implemented in MSVC10 however.

template <typename T>
struct base
{
private:
    ~base() {}
    friend T;
};

// Doesn't compile (base class destructor is private)
struct foo : base<bar> { ... };
Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • actually it doesn't work if the dtor is never called. – Abyx Aug 06 '14 at 12:58
  • @Abyx: Interestingly with gcc 4.9, if I use placement new to construct an object of type `struct S : base {}`, it complains about the *constructor* `S::S ()` being implicitly deleted due to `~base` being private. However in this case the destructor is never called. – Alexandre C. Aug 06 '14 at 23:30
  • More interestingly, gcc 4.8.1 does not complain at all! – Alexandre C. Aug 06 '14 at 23:36
  • 1
    placement new will call `base` dtor on exception – Abyx Aug 07 '14 at 08:28
  • @Abyx: I realize that, this is why the class is not instantiable (similar problem occurs with template constructors for class that have eg. a `unique_ptr` to an incomplete type), and eventually why my solution prevents `S` to inherit `base`. However, gcc 4.8 does not complain: this is either a bug or the default constructor may be allowed to be proven `noexcept`, but a further derived class with throwing ctor would have to know how to call derived dtor. Anyway, this is far from obvious without doing some standard exegesis. My belief is that gcc 4.8 is wrong and 4.9 is right here however. – Alexandre C. Aug 08 '14 at 10:47
11

You can use something like this:

template<class T> class Base {
protected:
   // derived classes must call this constructor
   Base(T *self) { }
};

class Foo : public Base<Foo> {
public:
   // OK: Foo derives from Base<Foo>
   Foo() : Base<Foo>(this) { }
};

class Moo : public Base<Foo> {
public:
   // error: constructor doesn't accept Moo*
   Moo() : Base<Foo>(this) { }
};

class Bar : public Base<Foo> {
public:
   // error: type 'Base<Bar>' is not a direct base of 'Bar'
   Bar() : Base<Bar>(this) { }
};
Amnon
  • 7,652
  • 2
  • 26
  • 34
  • It gets really verbose when Foo is itself a template. – Alexandre C. Dec 11 '10 at 17:53
  • 3
    @Alexandre: templates are verbose. – Amnon Dec 11 '10 at 18:06
  • yes they are, but in production code your code gets harder to use than the plain CRTP (I tried once to use something along these lines for the same reason as OP). – Alexandre C. Dec 11 '10 at 21:13
  • 1
    This solution is much cleaner than the private destructor trick -- for that it is: a trick that comes with its own problems, see [also here](http://stackoverflow.com/questions/33807223/securing-crtp-is-private-destructor-the-only-solution) – Walter Nov 19 '15 at 16:40
2
template<typename T, int arg1, int arg2>
struct Base
{
    typedef T derived_t;
};

struct Foo : Base<Foo, 1, 2>
{
    void check_base() { Base::derived_t(*this); } // OK
};

struct Bar : Base<Foo, 1, 2>
{
    void check_base() { Base::derived_t(*this); } // error
};

This code is based on Amnon's answer, but checking code don't contains name of derived class, so I can copy and paste it without changes.

Community
  • 1
  • 1
Abyx
  • 12,345
  • 5
  • 44
  • 76
0

There's no way of knowing the deriving type. You could enforce that Foo derived from Base<Foo>, but you can't enforce that no other classes also derive from that.

Puppy
  • 144,682
  • 38
  • 256
  • 465
0

I can use a macro

#define SOMENAMESPACE_BASE(type, arg1, arg2) type : Base<type, arg1, arg2>

but I don't want to use macros if better solution exists.

Abyx
  • 12,345
  • 5
  • 44
  • 76