在模板中指定允许的参数

时间:2012-08-15 02:15:48

标签: c++ templates generics sfinae static-assert

我可以准确指定模板可以接收的参数类型吗?例如,我想创建一个只能使用类A扩展的类来实例化的模板。在Java中,泛型支持:

class B<T extends A> { }

使用C ++中的模板可以实现类似的东西吗?

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

2 个答案:

答案 0 :(得分:3)

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

首先,通过隐藏的虚拟模板参数,该参数使用std::enable_if并以std::is_base_of<A, T>::value作为条件。如果后一个表达式推测为false,则嵌套typestd::enable_if中不存在。如果您在重载函数上使用它,则SFINAE意味着&#34;替换失败不是错误&#34;,并且有问题的重载将从可行功能集中移除。在这种情况下,没有其他类模板可以匹配您的调用,然后您会收到编译时错误。

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

其次,你可以在班级的正文中做一个简单的static_assertstd::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

答案 1 :(得分:3)

您可以使用static_assertis_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;来自Boost.TypeTraitsis_base_of,来自Boost.StaticAssertstatic_assert,来自Boost.EnableIfenable_if

相关问题