如何通过SFINAE测试班级内部班级的存在?

dra*_*oot 9 c++ sfinae template-specialization

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

jog*_*pan 4

回答最初的问题

\n

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

\n
template <typename T> struct HasXYZ <T,typename T::XYZ>\n{ static const bool value = true; };\n
Run Code Online (Sandbox Code Playgroud)\n

HasXYZ<A,A::XYZ>当有人使用某种数据类型的数据类型时就会生效A

\n

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

\n

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

\n

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

\n

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

\n
\n

补充问题的回答(更新后)

\n

考虑下面的简化:

\n
template <typename T, typename Check = void>\nstruct A\n{ static const bool value = false; };\n\ntemplate <typename T>\nstruct A<T,void>\n{ static const bool value = true; };\n
Run Code Online (Sandbox Code Playgroud)\n

主要定义void指定第二个模板参数的默认参数。但是,专业化(上面的第二个定义)定义了如果第二个模板参数确实class A的话,实际看起来是什么样子。 void

\n

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

\n

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

\n

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

\n

(*) 我在标准中没有找到实际上说得这么清楚的声明。但是 \xc2\xa714.1/10 描述了模板有多个声明(但只有一个主要定义)的情况:

\n
\n

(\xc2\xa714.1/10) 可与模板声明或定义一起使用的默认模板参数集是通过以相同方式合并定义(如果在范围内)和范围内的所有声明中的默认参数来获得的默认函数参数为 (8.3.6)。[ 例子:

\n
  template<class T1, class T2 = int> class A;\n  template<class T1 = int, class T2> class A;\n
Run Code Online (Sandbox Code Playgroud)\n

相当于

\n
  template<class T1 = int, class T2 = int> class A;\n
Run Code Online (Sandbox Code Playgroud)\n

]。

\n
\n

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

\n

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

\n

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

\n

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

\n