9

我正在尝试为具有特定名称的内部类的类提供不同的模板专业化。我从这里得到了一个线索并尝试了以下方法:

#include <iostream>

template< typename T, typename Check = void > struct HasXYZ
{ static const bool value = false; };

template< typename T > struct HasXYZ< T, typename T::XYZ >
{ static const bool value = true; };

struct Foo
{
  class XYZ {};
};

struct FooWithTypedef
{
  typedef void XYZ;
};

int main()
{
  // The following line prints 1, as expected
  std::cout << HasXYZ< FooWithTypedef >::value << std::endl;
  // The following line prints 0. Why?
  std::cout << HasXYZ< Foo >::value << std::endl;

  return 0;
}

如您所见,如果我在 中测试typedef-defined 类型FooWithTypedef,它可以工作。但是,如果类型是真正的内部类,则它不起作用。它也仅在typedef-ed 类型FooWithTypedef与初始模板声明中第二个参数的默认值匹配时才有效(void在我的示例中)。有人能解释一下这里发生了什么吗?专业化过程在这里如何运作?

4

3 回答 3

4

回答最初的问题

您在此处定义的模板专业化:

template <typename T> struct HasXYZ <T,typename T::XYZ>
{ static const bool value = true; };

HasXYZ<A,A::XYZ>当有人将该数据类型用于某些数据类型时将生效A

请注意,无论A是什么,A::XYZ都是完全独立于A. 内部类本身就是数据类型。当您A用作第一个模板参数时,编译器绝对没有理由假设您想要使用称为A:XYZ第二个参数的东西,即使存在该名称的内部类,即使这样做会导致编译器到与模板参数完全匹配的模板特化。模板特化是根据编码器提供的模板参数找到的,而不是基于更多可能的模板参数。

因此,当您使用 时HasXYZ<Foo>,它会回退到使用void第二个参数的默认模板参数。

不用说,如果您要HasXYZ<Foo,Foo:XYZ>明确使用,您将获得预期的输出。但这显然不是你想要的。

恐怕获得所需东西的唯一方法是std::enable_if(或以类似方式工作的东西)。


回答附加问题(更新后)

考虑下面的简化:

template <typename T, typename Check = void>
struct A
{ static const bool value = false; };

template <typename T>
struct A<T,void>
{ static const bool value = true; };

主要定义void为第二个模板参数指定默认参数。但是专业化(上面的第二个定义)定义了class A如果第二个模板参数真的 void.

这意味着,如果您A<int>在代码中使用,例如,默认参数将被补充,因此您得到A<int,void>,然后编译器会找到最合适的模板特化,即上面的第二个。

因此,虽然默认模板参数被定义为主模板声明的一部分,但使用它们并不意味着使用主模板定义。这基本上是因为默认模板参数是模板声明的一部分,而不是模板定义 (*)。

这就是为什么在您的示例中,当typedef void XYZ包含在 中时FooWithTypedef,第二个模板参数默认为void,然后找到最合适的特化。即使在模板特化中第二个参数被定义为T::XYZ而不是,这仍然有效void。如果在评估时这些相同,则将选择模板专业化(第 14.4 节“类型等效”)。

(*) 我没有在标准中找到一个声明如此清楚。但是有 §14.1/10,它描述了一个模板有多个声明(但只有一个主要定义)的情况:

(第 14.1/10 节)可用于模板声明或定义的默认模板参数集是通过将定义中的默认参数(如果在范围内)和范围内的所有声明以相同的方式合并默认函数参数来获得的(8.3.6)。[ 例子:

  template<class T1, class T2 = int> class A;
  template<class T1 = int, class T2> class A;

相当于

  template<class T1 = int, class T2 = int> class A;

]。

这表明默认模板参数背后的机制独立于用于识别模板最合适的特化的机制。

此外,还有两个现有的 SO 帖子也提到了这种机制:

如果类成员 typedef 不存在,此对模板专业化的回复以使用默认类型

类模板特化中模板参数的默认值

于 2012-08-22T07:38:39.507 回答
2

这是另一个检测内部类存在的版本:

#include <iostream>

template< typename T >
struct HasXYZ
{
  typedef char                 yes;
  typedef struct{ char d[2]; } no;

  template<typename T1>
  static yes test( typename T1::XYZ * );
  template<typename T1>
  static no test(...);

  static const bool value = ( sizeof( test<T>(0) ) == sizeof( yes ) );
};

struct Foo
{
  class XYZ {};
};
struct Bar
{
  class ABC {};
};

int main()
{
  std::cout << std::boolalpha << HasXYZ< Foo >::value << std::endl;
  std::cout << std::boolalpha << HasXYZ< Bar >::value << std::endl;
}
于 2012-08-22T06:47:56.150 回答
1

A::XYZ将需要void选择部分专业化,而类类型永远不会出现这种情况。使其工作的一种方法是使用虚假的依赖void类型名:

template<class T>
struct void_{ typedef void type; };

template<class T, class = void>
struct has_XYZ{ static bool const value = false; };

template<class T>
struct has_XYZ<T, typename void_<typename T::XYZ>::type>{
  static bool const value = true;
};

有关其工作原理的说明,请参阅此问题

于 2012-08-22T22:35:32.810 回答