如果类成员typedef不存在,则使用默认类型的模板特化

Fra*_*ank 15 c++ templates

我正在尝试编写使用模板参数的成员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;
}
Run Code Online (Sandbox Code Playgroud)

我希望这会打印"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> { ... };
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为enable_if <true>的类型为typedef,但enable_if <false>则不然.

我不明白这与我的版本有什么不同,而不是使用enable_if我直接使用T :: Type.如果T :: Type存在与上面示例中的enable_if <true> :: type不同,是否会导致选择特化?如果T :: Type不存在,那就不一样与enable_if <false> :: type不存在并导致在上面的例子中选择默认版本?

Geo*_*che 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;
};
Run Code Online (Sandbox Code Playgroud)


Joh*_*itb 8

要回答你的补充 - 你的specialization参数传递成员typedef并期望它void作为类型产生.这没有什么神奇之处 - 它只使用默认参数.让我们看看它是如何工作的.如果你说Get_Type<Foo>::type,编译器使用默认参数Enable,即void,类型名称变为Get_Type<Foo, void>::type.现在,编译器检查是否有任何部分特化匹配.

您的部分特化参数列表<T, typename T::Type>是从原始参数列表中推导出来的<Foo, void>.这将演绎TFoo事后代用品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; 
};
Run Code Online (Sandbox Code Playgroud)

现在这将像您期望的那样工作.使用MPL,您可以使用always而不是tovoid

typename apply< always<void>, typename T::type >::type
Run Code Online (Sandbox Code Playgroud)