【问题标题】:Template specialization to use default type if class member typedef does not exist如果类成员 typedef 不存在,则模板特化使用默认类型
【发布时间】:2010-06-09 18:09:29
【问题描述】:

我正在尝试编写使用模板参数的成员 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 存在。但是,这不会发生。

谁能解释这里发生了什么以及如何解决它,或者以其他方式实现相同目标?

谢谢。

编辑:

Georg 提供了另一种方法,但我仍然很好奇为什么这不起作用。根据 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 有 type 作为 typedef,但 enable_if 没有。

我不明白这与我的版本有何不同,我只是直接使用 T::Type 而不是使用 enable_if。如果存在 T::Type 是否与上面示例中的 enable_if::type 相同并导致选择专业化?而如果 T::Type 不存在,那岂不是和 enable_if::type not existing 一样,导致上面例子中选择了默认版本?

【问题讨论】:

  • 目标是 Get_Type::Type 如果存在则为 T::Type,如果不存在则为 DefaultType。

标签: c++ templates


【解决方案1】:

回答您的添加 - 您的专业化参数传递成员 typedef 并期望它产生 void 作为类型。这没有什么神奇之处——它只是使用一个默认参数。让我们看看它是如何工作的。如果你说Get_Type&lt;Foo&gt;::type,编译器使用Enable的默认参数,即void,类型名称变为Get_Type&lt;Foo, void&gt;::type。现在,编译器检查是否有任何偏特化匹配。

您的部分特化的参数列表&lt;T, typename T::Type&gt; 是从原始参数列表&lt;Foo, void&gt; 推导出来的。这会将T 推导出为Foo,然后将Foo 代入专业化的第二个参数,为您的部分专业化生成&lt;Foo, NonDefaultType&gt; 的最终结果。但是,这与原始参数列表 &lt;Foo, void&gt; 完全不匹配!

您需要一种方法来生成 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

【讨论】:

  • 谢谢约翰内斯。我没有意识到类型本身必须是 void 才能匹配,但这很有意义。
【解决方案2】:

您可以使用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;
};

【讨论】:

  • 谢谢乔治,这行得通。我用另一个关于为什么我的实现不起作用的问题编辑了我的问题,因为它似乎与 enable_if 本身所基于的原则相同。 enable_if 有一个 typedef,但 enable_if 没有。我不明白为什么这与 T::Type 是否存在不一样。
  • @Frank:约翰内斯已经在另一个答案中很好地回答了这个问题。
【解决方案3】:

第一步:停止使用“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。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多