C++: How to override method of a specific class with same interface

You can always define intermediate classes that declare their own interfaces:

template<typename T>
struct Foo
{
    virtual ~Foo() = default;
    virtual void foo() = 0;
};

struct ProxyFooInt : public Foo<int>
{
    virtual void fooInt() = 0;
    
    void foo() override
    {
        return fooInt();
    }
};

struct ProxyFooDouble : public Foo<double>
{
    virtual void fooDouble() = 0;
    
    void foo() override
    {
        return fooDouble();
    }
};

struct Derived : public ProxyFooInt, public ProxyFooDouble
{
    void fooInt() override
    {
        std::cout << "Foo<int>::foo()" << std::endl;
    }

    void fooDouble() override
    {
        std::cout << "Foo<double>::foo()" << std::endl;
    }
};

A more advanced solution would be to use CRTP:

template<typename D>
struct CrtpFooInt : public Foo<int>
{
    void foo() override
    {
        return static_cast<D*>(this)->fooInt();
    }
};

template<typename D>
struct CrtpFooDouble : public Foo<double>
{
    void foo() override
    {
        return static_cast<D*>(this)->fooDouble();
    }
};

struct Derived : public CrtpFooInt<Derived>, public CrtpFooDouble<Derived>
{
    void fooInt()
    {
        std::cout << "Foo<int>::foo()" << std::endl;
    }

    void fooDouble()
    {
        std::cout << "Foo<double>::foo()" << std::endl;
    }
};

Another creative way to solve this problem would be to use an ability to add some overloading to distinguish between different invocations of foo():


template<typename T>
struct Foo
{
    virtual ~Foo() = default;
    virtual void foo() = 0;
};

template<typename T>
struct Tag{};

template<typename T>
struct Overloader : public Foo<T>
{
    virtual void foo(Tag<T>) = 0;

    void foo() override
    {
        foo(Tag<T>{});
    }
};

struct Derived : public Overloader<int>, public Overloader<double>
{
    void foo(Tag<int>) override
    {
        std::cout << "Foo<int>::foo()" << std::endl;
    }

    void foo(Tag<double>) override
    {
        std::cout << "Foo<double>::foo()" << std::endl;
    }
};