Declaring template functions in a C++ interface?

Viewed 1154

For the sake of a demonstration imagine that I have some animal classes, each derived from an 'animal' class, each 'knowing' what type they are, and each having some sort of unique ability:

enum class animal_type { antelope, bear, cat };

class animal
{
};

class antelope : public animal
{
public:
    static const animal_type type = animal_type::antelope;
    void run() { std::cout << "antelope runs\n"; };
};

class bear : public animal
{
public:
    static const animal_type type = animal_type::bear;
    void roar() { std::cout << "bear roars\n"; };
};

class cat : public animal
{
public:
    static const animal_type type = animal_type::cat;
    void meow() { std::cout << "cat meows\n"; };
};

Now, I want to be able to retrieve animals based on their type:

class animal_getter
{
public:
    animal& get(animal_type t)
    {
        static antelope s_antelope;
        static bear s_bear;
        static cat s_cat;

        switch (t)
        {
            case animal_type::antelope:
                return s_antelope;

            case animal_type::bear:
                return s_bear;

            case animal_type::cat:
                return s_cat;
        }
    }
};

And finally, it would be nice to get the actual type of animal back, to make the calling syntax nicer:

template<typename T>
T& get()
{
    return static_cast<T&>(get(T::type));
}

Now I can write something like this:

animal_getter ag;
ag.get<antelope>().run();

rather than the wordier:

animal_getter ag;
static_cast<antelope&>(ag.get(animal_type::antelope)).run();

I hope there's nothing too unreasonable about that. But now I want to be able to unit test getting an animal, so ideally it will be possible to fake the animal_getter class (imagine the actual implementation accesses a database or something which you don't want in a unit test, hence the fake). So it would be nice to define an interface for 'animal getter' classes, and then create a fake that implements the interface. And here's the problem, can this interface be written? This isn't going to compile:

struct IAnimalGetter
{
    virtual template<typename T> T& get() = 0;
};

Is it possible to rescue this idea or can template functions never be declared a virtual for the purpose of defining an interface that includes them?

If the idea is a non-starter, at what point did this start to go wrong? Was it when the template function was written which does its own casting? Should I have stopped at something which returns an animal object and then have the caller be responsible for the cast?

2 Answers
Related