Why doesn't this curiously recurring template pattern example compile?

108 Views Asked by At

I believe this is an example of the curiously recurring template pattern. It seems to me that this should compile, but it does not. This is with Clang in Xcode 8.3.

template<class T>
class Fungeable
{
public:
    virtual ~Fungeable() {}
    virtual bool funge( const Fungeable<T>& inFungeable ) const = 0;
};

class Blarg : public Fungeable<Blarg>
{
public:
    virtual bool funge( const Blarg& inFungeable ) const override { return true; }
};

int main(int argc, const char * argv[]) {
    Blarg b;
    Blarg x;
    return static_cast<int>( b.funge( x ) );
}

It seems like this should work because Blarg is a Fungeable. But I get the error 'funge' marked 'override' but does not override any member functions

If I change the signature of Blarg::funge() to take a Fungeable<Blarg> like this:

class Blarg : public Fungeable<Blarg>
{
public:
    virtual bool funge( const Fungeable<Blarg>& inFungeable ) const override { return true; }
};

Then it compiles.

Shouldn't the first version work since Blarg is, by definition, a Fungeable<Blarg>?

1

There are 1 best solutions below

2
On BEST ANSWER

Co-variant parameter types don't, never have, and most probably never will work in C++. You may think your example is "obviously" safe. But the feature is un-safe in general, and adding support for such a small use case won't happen.

To illustrate:

struct food {};
struct grass : food {};

struct animal {
  virtual void eat(food&) = 0;
};

void feed(animal& a) {
  a.eat(grass{});
}

So far so good. Now, let's add more to the hierarchy, assuming we have co-variant parameter types:

struct meat : food {};

struct lion : animal {
  void eat(meat&) {/*...*}
};

What happens when I pass a lion object to feed? It can't handle grass as food, but it is-a animal. This breaks the Liskov substitution principle.