4

我可以准确地指定模板可以接收什么样的参数吗?例如,我想创建一个模板,该模板只能用属于或扩展类的类来实例化A。在 Java 中,泛型通过以下方式支持这一点:

class B<T extends A> { }

可以用 C++ 中的模板实现类似的功能吗?

template <typename T (?)> class B { }
4

2 回答 2

3

有两种方法可以做到这一点。

首先,通过std::enable_ifstd::is_base_of<A, T>::valueas 条件一起使用的隐藏虚拟模板参数。如果后一个表达式计算为false,则嵌套type在 中不存在std::enable_if。如果您在重载函数上使用它,SFINAE 则意味着“替换失败不是错误”,并且有问题的重载将从可行函数集中删除。但是在这种情况下,没有其他类模板可以匹配您的调用,然后您确实会收到编译时错误。

SFINAE 是一个非常微妙的机制,很容易出错。例如,如果您有多个具有不同 SFINAE 条件的类专业化,您必须确保它们都不重叠,否则您会产生歧义。

其次,您可以在类的主体内部做一个简单static_assert的操作。std::is_base_of<A,T>::value与 SFINAE 方法相比,此方法的优点是您还可以指定更易读的错误消息。一个缺点是您总是会收到错误,并且您不能静默地抑制此特定模板并选择另一个模板。但总的来说,我认为在你的情况下推荐这种方法。

#include<type_traits>

class A {};
class C: public A {};
class D {};

// first alternative: SFINAE on hidden template parameter
template
<
    typename T, 
    typename /* dummy */ = typename std::enable_if< 
        std::is_base_of<A, T>::value
    >::type
>
class B
{
};

// second alternative: static_assert inside class
template
<
    typename T
>
class E
{
    static_assert(std::is_base_of<A, T>::value, "A should be a base of T");
};

int main()
{
    B<A> b1;
    B<C> c1;
    //B<D> d1; // uncomment this line to get a compile-time error

    E<A> b2;
    E<C> c2;
    //E<D> d2; // uncomment this line to get a compile-time error

    return 0;
}

正如评论中所指出的,您可以使用像样的 C++11 编译器(VC++ 2010 或更高版本,gcc 4.5 或更高版本)或 Boost 或 TR1 库来获取<type_traits>功能。但是请注意,std::is_base_of<A, A>::value评估结果为true,但旧的评估boost::is_base_of<A, A>::value结果为false

于 2012-08-15T06:50:39.453 回答
3

你可以用static_assertand做到这一点is_base_of

#include <type_traits>
template<typename T> class D {
    static_assert(std::is_base_of<A, T>::value, "must be derived from A");
};

或者您可以使用enable_if

#include <type_traits>
template<typename T, typename = void> class D;
template<typename T> class D<T, typename std::enable_if<std::is_base_of<A, T>::value>::type> {
};

对于 C++03,您可以使用 boost;is_base_of来自Boost.TypeTraitsstatic_assert来自Boost.StaticAssertenable_if来自Boost.EnableIf

于 2012-08-15T06:51:58.643 回答