我可以准确指定模板可以接收的参数类型吗?例如,我想创建一个只能使用类A
扩展的类来实例化的模板。在Java中,泛型支持:
class B<T extends A> { }
使用C ++中的模板可以实现类似的东西吗?
template <typename T (?)> class B { }
答案 0 :(得分:3)
有两种方法可以做到这一点。
首先,通过隐藏的虚拟模板参数,该参数使用std::enable_if
并以std::is_base_of<A, T>::value
作为条件。如果后一个表达式推测为false
,则嵌套type
在std::enable_if
中不存在。如果您在重载函数上使用它,则SFINAE意味着&#34;替换失败不是错误&#34;,并且有问题的重载将从可行功能集中移除。在这种情况下,没有其他类模板可以匹配您的调用,然后您会收到编译时错误。
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
。
答案 1 :(得分:3)
您可以使用static_assert
和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;来自Boost.TypeTraits的is_base_of
,来自Boost.StaticAssert的static_assert
,来自Boost.EnableIf的enable_if
。