我正在尝试编写使用模板参数的成员typedef的代码,但是如果模板参数没有该typedef,则希望提供默认类型。我试过的一个简单例子是:
struct DefaultType { DefaultType() { printf("Default "); } };
struct NonDefaultType { NonDefaultType() { printf("NonDefault "); } };
struct A {};
struct B { typedef NonDefaultType Type; };
template<typename T, typename Enable = void> struct Get_Type {
typedef DefaultType Type;
};
template<typename T> struct Get_Type< T, typename T::Type > {
typedef typename T::Type Type;
};
int main()
{
Get_Type<A>::Type test1;
Get_Type<B>::Type test2;
}
我希望这会打印“Default NonDefault”,而是打印“Default Default”。我的期望是main()中的第二行应该与Get_Type的专用版本匹配,因为B :: Type存在。但是,这不会发生。
任何人都可以解释这里发生了什么以及如何解决它,或者其他方式来实现同样的目标吗?
谢谢。
编辑:
格奥尔格提供了另一种方法,但我仍然很好奇为什么这不起作用。根据boost enable_if文档,为不同类型专门化模板的方法如下:
template <class T, class Enable = void>
class A { ... };
template <class T>
class A<T, typename enable_if<is_integral<T> >::type> { ... };
template <class T>
class A<T, typename enable_if<is_float<T> >::type> { ... };
这是因为enable_if&lt;真&gt;将type作为typedef,但enable_if&lt;假&gt;没有。
我不明白这与我的版本有什么不同,而不是使用enable_if我直接使用T :: Type。如果T :: Type存在则不会与enable_if&lt;在上面的示例中,是&gt; ::类型并导致选择特化?如果T :: Type不存在,那就不一样与enable_if&lt; false&gt; ::类型不存在并导致在上面的示例中选择默认版本?
答案 0 :(得分:8)
您可以使用SFINAE:
来实现这一目标template<class T> struct has_type {
template<class U> static char (&test(typename U::Type const*))[1];
template<class U> static char (&test(...))[2];
static const bool value = (sizeof(test<T>(0)) == 1);
};
template<class T, bool has = has_type<T>::value> struct Get_Type {
typedef DefaultType Type;
};
template<class T> struct Get_Type<T, true> {
typedef typename T::Type Type;
};
答案 1 :(得分:8)
要回答您的补充 - 您的专业化参数会传递成员typedef,并期望它作为类型生成void
。这没有什么神奇之处 - 它只使用默认参数。让我们看看它是如何工作的。如果您说Get_Type<Foo>::type
,编译器将使用默认参数Enable
,即void
,类型名称变为Get_Type<Foo, void>::type
。现在,编译器检查是否有任何部分特化匹配。
您的部分特化参数列表<T, typename T::Type>
是从原始参数列表<Foo, void>
中推导出来的。这会将T
推导为Foo
,然后将Foo
替换为专精的第二个参数,从而为您的部分专业化产生<Foo, NonDefaultType>
的最终结果。但是,它根本不匹配原始参数列表<Foo, void>
!
您需要一种方法来生成void
类型,如下所示:
template<typename T>
struct tovoid { typedef void type; };
template<typename T, typename Enable = void> struct Get_Type {
typedef DefaultType Type;
};
template<typename T>
struct Get_Type< T, typename tovoid<typename T::Type>::type > {
typedef typename T::Type Type;
};
现在这将按照您的预期运作。使用MPL,您可以使用always
代替tovoid
typename apply< always<void>, typename T::type >::type
答案 2 :(得分:4)
第一步:停止使用“Type”并使用mpl标准“type”。
BOOST_MPL_HAS_XXX_DEF(Type)
template < typename T >
struct get_type { typedef typename T::Type type; };
template < typename T >
struct calculate_type : boost::mpl::if_
<
has_Type<T>
, get_type<T>
, boost::mpl::identity<default_type>
>::type {}
typedef calculate_type<A>::type whatever;
如果在元函数中使用“type”而不是“Type”,则不需要提取器“get_type”来转换它,在这种情况下可能只返回T.