我有以下代码:
#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;
}
当编译器尝试使用参数B<int>
实例化第二个部分特化时,std::is_base_of<C<int>, B<int>>::value
为true
,因此std::enable_if<...>::type
返回void
(默认类型,如果没有指定)。这导致了一个模糊的部分特化&#34;因为编译器无法在第一和第二部分特化之间做出决定。到现在为止还挺好。但是,当我将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 here上进行了测试和验证。
答案 0 :(得分:2)
在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>
不匹配,则将使用第二个更一般的特化。无论哪种方式,你都不会得到模棱两可的错误。
有关详细信息,请参阅partial template specialization
中有关部分排序的讨论现在让我们考虑std::enable_if<std::is_base_of<...>::value>::type
案例。
你仍然有两个专业化,但第二个专业化现在以enable_if为条件,而enable_if又取决于参数T.
A<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;
A<int> y;
在std::enable_if<true>
情况下,这将编译,两个声明都将调用“there”构造函数。
在更复杂的情况下,x
的声明将编译并调用“there”构造函数,但y
的声明将产生编译器错误。
没有int::Var_t
因此std::enable_if
表达式将导致替换失败,SFINAE将隐藏该专业化。这意味着不会有任何适合int
的专业化,您将收到错误aggregate ‘A<int> y’ has incomplete type and cannot be defined