模板函数只接受某类继承人

时间:2014-12-20 02:35:18

标签: c++ templates inheritance

在C ++中,假设我有一些类mom。我知道我可以创建一个接受任何类的模板函数,例如:

template <class T> void Iacceptanything(T x)
{
  // Do something
}

现在,这很好用,但是我想创建一个更加限制的模板类,它接受T任何继承自类mom的类型。我想让函数接受mom作为唯一的参数类型,但在该函数中我需要用参数构建一个模板对象,因此我需要保留它的类型(即,我的对象不应该是“被修剪”只是它是mom)的继承人。

我需要的是:

template <class T:mom> void Iacceptonlysonsofmom(T x)
{
    // Do something
}

这有可能吗?

3 个答案:

答案 0 :(得分:4)

使用std::enable_ifstd::is_base_of

#include <type_traits>
#include <iostream>

class Base { };
class Derived : public Base { };
class NotDerived { };

// If the return type of foo() is not void, add where indicated.
template <typename T>
typename std::enable_if<std::is_base_of<Base, T>::value /*, some_type*/>::type
foo(T) {
    std::cout << "Derived from Base." << std::endl;
}

// If the return type of foo() is not void, add where indicated.
template <typename T>
typename std::enable_if<!std::is_base_of<Base, T>::value /*, some_type*/>::type
foo(T) {
    std::cout << "Not derived from Base." << std::endl;
}

int
main() {
    Derived d;
    NotDerived nd;

    foo(d);
    foo(nd);
}

答案 1 :(得分:0)

像这样使用std::enable_ifstd::is_base_of

template <typename T, typename = enable_if<is_base_of<mom, T>::value>::type>
void Iacceptonlysonsofmom(T x)
{
}

如果您无法使用C++11,则可以使用Boost enable_if_cis_base_of来实现相同目标。在上面的代码中,您需要将enable_if替换为enable_if_c,并确保使用适当的命名空间。

答案 2 :(得分:0)

正如其他人所建议您可以使用enable_ifSFINAE (Substition Failure Is Not An Error),但这可能会有问题。它可能在编译器之间很脆弱,如果没有正确完成可能会导致ODR出现问题,可能会导致与从其他作用域(ADL)引入的函数发生冲突,并且可能不是最佳选择。

您确实有一些不太变幻无常的替代选项,可能会让您的代码更易于理解和维护。一个选项(如评论中所述)是使用static_assertis_base_of。到目前为止,这是(恕我直言)最简单的解决方案。此方法允许您维护函数的单个实现,并在类型不是基类Mom或从中派生时强制编译时失败。

#include <type_traits>

struct Mom { };
struct Child : Mom { };
struct Uncle { };

template <typename T>
void AcceptOnlyMomAndChildren(T)
{
    static_assert(std::is_base_of<Mom, T>::value, "Not derived from Base.");
}

int main()
{
    Child child;
    Uncle uncle;

    AcceptOnlyMomAndChildren(child);
    AcceptOnlyMomAndChildren(uncle);    //  Fails
}

如果需要提供多个函数来处理非使用标记调度从特定基类派生的类型可能更有意义。这在编译器中变得不那么易变,并且必须更容易看到。如果您需要为从mom派生的类型(在您的示例中)提供特殊处理,也可以轻松扩展它。

#include <type_traits>
#include <iostream>

struct Mom { };
struct Child : Mom { };
struct Uncle { };

template <typename T>
void Func(T&, std::true_type)
{
    std::cout << "Derived from Base." << std::endl;
}

template <typename T>
void Func(T&, std::false_type)
{
    std::cout << "Not derived from Base." << std::endl;
}

template <typename T>
void Func(T& arg)
{
    Func(
        arg,
        typename std::conditional<
            std::is_base_of<Mom, T>::value,
            std::true_type, std::false_type>::type());
}


int main()
{
    Child child;
    Uncle uncle;

    Func(child);
    Func(uncle);
}

在上面的示例中,std::conditional用于指定附加参数的类型,以确定调用Func的哪个实现。您可以使用自己的类型特征方案替换它,以便在必要时支持其他实现。