我可以准确地指定模板可以接收什么样的参数吗?例如,我想创建一个模板,该模板只能用属于或扩展类的类来实例化A
。在 Java 中,泛型通过以下方式支持这一点:
class B<T extends A> { }
可以用 C++ 中的模板实现类似的功能吗?
template <typename T (?)> class B { }
我可以准确地指定模板可以接收什么样的参数吗?例如,我想创建一个模板,该模板只能用属于或扩展类的类来实例化A
。在 Java 中,泛型通过以下方式支持这一点:
class B<T extends A> { }
可以用 C++ 中的模板实现类似的功能吗?
template <typename T (?)> class B { }
有两种方法可以做到这一点。
首先,通过std::enable_if
与std::is_base_of<A, T>::value
as 条件一起使用的隐藏虚拟模板参数。如果后一个表达式计算为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
。
你可以用static_assert
and做到这一点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.TypeTraits,static_assert
来自Boost.StaticAssert,enable_if
来自Boost.EnableIf。