【问题标题】:Template function that accepts only heirs of some class仅接受某个类的继承人的模板函数
【发布时间】:2014-12-20 02:35:17
【问题描述】:

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

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

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

我需要的是这样的:

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

这可能吗?

【问题讨论】:

  • 您可以使用std::enable_ifstatic_assert(),但我建议不要限制模板参数;如果您使用一些依赖于mom 的功能,那么模板将无法与其他对象进行实例化(除非它们提供兼容的接口——如果它们提供了兼容的接口,那么为什么它会对您产生任何影响?),但如果您不这样做'没有理由将其限制为仅适用于 mom 对象。

标签: c++ templates inheritance


【解决方案1】:

使用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);
}

【讨论】:

  • 您可以删除std::enable_if 的显式第二个参数,因为它默认为void
【解决方案2】:

像这样使用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 并确保使用适当的命名空间。

【讨论】:

    【解决方案3】:

    正如其他人建议的那样,您可以使用enable_ifSFINAE (Substition Failure Is Not An Error),但这可能会出现问题。它在编译器中可能很脆弱,如果处理不当可能会导致 ODR 出现问题,可能会导致与从其他范围 (ADL) 中提取的函数发生冲突,并且可能不是最佳选择。

    您确实有几个替代选项,它们不那么变化无常,并且可能使您的代码更易于理解和维护。一种选择(如 cmets 中所述)是使用 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 的哪个实现。如有必要,您可以将其替换为您自己的类型特征方案以支持其他实现。

    【讨论】:

      猜你喜欢
      • 2010-10-26
      • 2019-12-14
      • 1970-01-01
      • 2016-03-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多