Search code examples
c++templatesinheritanceboostenable-if

Disable method override in template derived class


Would it be possible to disable the Foo() override in the derived class (by means of std::enable_if or some boost magic), in case T is not of a certain type, without having to write a template specialization for class Derived?

Bonus points: could the override be disabled if T does not define a certain method?

Here is my SSCCE:

#include <iostream>
#include <string>

class Base
{
public:
    virtual std::string Foo()
    {
        return "Base";
    }
};

template <typename T>
class Derived : public Base
{
public:
    virtual std::string Foo() override
    {
        return "Derived";
    }
};

int main()
{
    Derived<int> testInt;
    std::cout << testInt.Foo() << std::endl;

    Derived<float> testFloat;
    std::cout << testFloat.Foo() << std::endl;//I would like this to print 'Base'
}

UPDATE:

Thank you for the wonderful solutions, but I wasn't able to adapt them to my real code. The following example should provide a better idea of what I'm trying to achieve:

#include <iostream>
#include <string>

class Object
{
public:
    void Test()
    {
        std::cout << "Test" << std::endl;
    }
};

class EmptyObject
{
};

class Base
{
public:
    virtual std::string Foo()
    {
        return "Base";
    }
};

template <typename T>
class Derived : public Base
{
public:
    virtual std::string Foo() override
    {
        m_object.Test();
        return "Derived";
    }

private:
    T m_object;
};

int main()
{
    Derived<Object> testObject;
    std::cout << testObject.Foo() << std::endl;

    Derived<EmptyObject> testEmpty;
    std::cout << testEmpty.Foo() << std::endl;
}

Solution

  • I would do this by creating two functions that Derived::Foo can delegate to conditionally based on whether T = float. One would contain the real Derived::Foo implementation, while the other would call Base::Foo.

    template <typename T>
    class Derived : public Base
    {
    public:
        virtual std::string Foo() override
        {
            return do_Foo(std::is_same<T, float>{});
        }
    
    private:
        std::string do_Foo(std::false_type)
        {
            return "Derived";
        }
        std::string do_Foo(std::true_type)
        {
            return Base::Foo();
        }
    };
    

    Live demo


    It seems what you actually want to do is call the Derived<T>::Foo() implementation only if T defines a certain member function, otherwise Base::Foo() should be called. This can be done using expression SFINAE.

    template <typename T>
    class Derived : public Base
    {
    public:
        std::string Foo() override
        {
            return do_Foo(true);
        }
    private:
        template<typename U = T>
        auto do_Foo(bool)
            -> decltype(std::declval<U>().test(), void(), std::string())
        {
            return "Derived";
        }
        std::string do_Foo(int)
        {
            return Base::Foo();
        }
    };
    

    Live demo

    In the code above, if the type T does not define a member function named test(), the do_Foo(bool) member function template will not be viable. On the other hand, if T::test() does exist, then do_Foo(bool) will be selected because the boolean value being passed to do_Foo by Foo makes it a better match as compared to do_Foo(int).

    A detailed explanation of what's going on within the decltype expression in the trailing return type can be found here.