禁用模板派生类中的方法覆盖

时间:2014-06-16 17:12:39

标签: c++ templates inheritance boost enable-if

是否可以在派生类中禁用Foo()覆盖(通过std::enable_if或某些增强魔法),如果T不是某种类型,则无需编写class Derived的模板专精化?

奖励积分:如果T没有定义某种方法,是否可以禁用覆盖?

这是我的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'
}

更新

感谢您提供精彩的解决方案,但我无法将其应用于我的真实代码。以下示例应该更好地了解我想要实现的目标:

#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;
}

4 个答案:

答案 0 :(得分:1)

而不是模板专门化类,您可以直接模板化该方法:(https://ideone.com/gYwt5r

template<> std::string Derived<float>::Foo() { return Base::Foo(); }

我只看到类的模板特化,通过向虚拟方法添加T来禁用将来覆盖,具体取决于final

答案 1 :(得分:1)

我会通过创建Derived::Foo可根据T = float有条件委托的两个函数来实现此目的。一个将包含真正的Derived::Foo实现,而另一个将调用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


看起来你真正想做的只是在Derived<T>::Foo()定义某个成员函数时才调用T实现,否则应该调用Base::Foo()。这可以使用表达式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

在上面的代码中,如果类型T未定义名为test()的成员函数,则do_Foo(bool)成员函数模板将不可行。另一方面,如果T::test()确实存在,那么将选择do_Foo(bool),因为do_Foo传递给Foo的布尔值使其与{相比}更好地匹配{1}}。

可以在here找到尾随返回类型中do_Foo(int)表达式内发生的内容的详细说明。

答案 2 :(得分:1)

如果您需要在编译时限制某种类型,则可以将std::enable_ifstd::is_same一起使用:

typename std::enable_if<std::is_same<T, float>::value, std::string>::type 
virtual Foo() override
{
    return "Derived";
}

或者,如果模板类型不是您要查找的类型,则可以轻松地将调用重定向到Base方法,仍然使用std::is_same

virtual std::string Foo() override
{
    return std::is_same<T, float>::value ? Base::Foo() : "Derived";
}

对于 Bonus ,您可以使用decltype获取此SO answer的特征,并使用bar()进行调整,以获取方法template <typename T> class has_bar { typedef char one; typedef long two; template <typename C> static one test(decltype(&C::bar) ) ; template <typename C> static two test(...); public: enum { value = sizeof(test<T>(0)) == sizeof(char) }; };

virtual std::string Foo() override
{
    return has_bar<T>::value ? "Derived" : Base::Foo() ;
}

限制是您不能对参数或返回类型设置约束。

has_bar

注意:

您也可以像我的第一个示例一样使用enable_if和{{1}},以便在编译时禁用它。

答案 3 :(得分:1)

您可以在层次结构中添加中间类:

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

template <typename T>
class Intermediate : public Base
{
    // common operations with m_object

protected: // not private!
    T m_object;
};

template <typename T, typename = bool>
class Derived : public Intermediate<T> {};

template <typename T>
class Derived<T, decltype(std::declval<T>().Test(), void(), true)>
    : public Intermediate<T>
{
public:
    virtual std::string Foo() override
    {
        this->m_object.Test(); // this-> is necessary here!
        return "Derived";
    }
};

完整示例与clang 3.4g++ 4.8.2成功编译。