我有以下代码:
#include <iostream>
template <class T, typename U = void> class A;
template <class T>
class C
{
public:
typedef T Var_t;
};
template <class T>
class B : public C<T>
{
};
template <class T>
class A<B<T>>
{
public:
A() { std::cout << "Here." << std::endl; }
};
template <class T>
class A<T, typename std::enable_if<
std::is_base_of<C<typename T::Var_t>, T>::value>
::type>
{
public:
A() { std::cout << "There." << std::endl;}
};
int main()
{
A<B<int>> a;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当编译器尝试使用参数实例化第二个部分专业化时B<int>,std::is_base_of<C<int>, B<int>>::valueis true,因此std::enable_if<...>::type返回值void(如果未指定默认类型,则返回默认类型)。由于编译器无法在第一部分专业化和第二部分专业化之间做出决定,因此会导致“模棱两可的部分专业化”错误。到目前为止,一切都很好。但是,当我将代码替换std::enable_if为时true(即,第二部分专业化只是template <class T> class A<T, typename std::enable_if<true>::type>),代码将编译并运行。输出"Here",表明选择了第一个专业化。
我的问题是:如果它们都void最终求和,为什么的行为std::enable_if<true>::type不同于的行为std::enable_if<std::is_base_of<...>::value>::type?
此行为已在此处的 Ideone上进行了测试和验证。
如果std::enable_if<true>::type您的代码定义了 A 类的两个特化,即:
A<B<T>, void>A<T, std::enable_if<true>::type>。 这两个专业彼此截然不同。第一个专业化狭隘地关注类型B<T>,而第二个专业化更普遍地适合任何类型。此外,在第二个专业化中,std::enable_if表达式不T以任何方式依赖。
对于任何声明,A<X> a;类型X要么匹配B<something>,要么不匹配。如果匹配,B<something>则将使用第一个专业化,因为它“更专业化”。如果 X 不匹配,B<something>则将使用第二个更通用的专业化。不管怎样,你都不会得到模棱两可的错误。
有关更多详细信息,请参阅部分模板专业化中对部分排序的讨论
\n\n现在让我们考虑一下这个std::enable_if<std::is_base_of<...>::value>::type情况。
您仍然有两个专业化,但第二个专业化现在以enable_if为条件,而enable_if又取决于参数T。
\n\nA<B<T>, void>A<T, std::enable_if<...>>。 该类型B<int>现在与两个专业化相匹配(在某种程度上)。显然它与A<B<T>>, void>专门化匹配,但它也与A<T, std::enable_if...>>专门化匹配,因为B<int>它是满足表达式所施加的条件的类型std::enable_if。
这为您提供了两个同样有效的专业化,它们是变量声明的候选者a,因此您会收到“不明确的部分专业化”错误。
如果您再添加两个声明,可能会让这一切变得更加具体main
A<C<int>> x;\nA<int> y;\nRun Code Online (Sandbox Code Playgroud)\n\n在这种std::enable_if<true>情况下,这将编译并且两个声明都将调用“there”构造函数。
在更复杂的情况下, 的声明x将编译并调用“there”构造函数,但声明y将会出现编译器错误。
不存在int::Var_t,因此std::enable_if表达式将出现替换失败,并且 SFINAE 将隐藏该专业化。这意味着不会有任何适合的专业化,int您将收到错误aggregate \xe2\x80\x98A<int> y\xe2\x80\x99 has incomplete type and cannot be defined