SCNLookAtConstraint
第一个案例输出#include <iostream>
#include <type_traits>
template<typename T>
struct A
{
using m = std::remove_pointer_t<T>&;
};
template
<
typename T,
typename = std::void_t<>
>
struct Test
{
enum { value = 0 };
};
template<typename T>
struct Test<T, typename A<T>::m>
{
enum { value = 1 };
};
int main()
{
std::cout << Test<void*&>::value; // ok, output 0
std::cout << Test<void*>::value; // error : cannot form a reference to 'void'
}
,表示选择了主要模板。所以,我认为第二种情况也应该选择主要模板而不是专用模板;那么,不应该有错误。
预计0
可以;让我感到惊讶的是Test<void*&>
应该不行!
为什么 SFINAE 在后一种情况下不起作用?
答案 0 :(得分:5)
你的第二个案例是一个很难的错误。
只有函数类型或其模板参数类型的直接上下文中的类型和表达式中的失败才是SFINAE错误。如果对替换类型/表达式的求值导致副作用,例如某些模板特化的实例化,隐式定义的成员函数的生成等,那些副作用中的错误将被视为硬错误。
第一种情况还可以,因为如果remove_pointer
为T
,void*&
无效,则m
为void*&
,因为参考资料崩溃并且引用指向void的指针是有效类型,而对void的引用则不是。
第一种情况中的类型仍然是Test<void*&, void>
而不是Test<void*&, void*&>
,因为您只指定了第一个模板参数。
第二种情况失败但不是第一种情况的原因是编译器必须实例化专用模板,因为第二个参数是非推导的上下文,因此编译器无法立即判断专业化是否是更好的匹配。但在第二种情况下,实例化会产生硬错误,而在第一种情况下却没有。
仍然选择主模板,因为它是一个更好的匹配(同时仍然会实例化专用模板以检查它是否匹配)。
注意:我不能说专业化是否实际上是完全实例化的,或者编译器是否只是查找typename A<T>::m
以检查此专业化是否更好匹配。结果却是一样的。如果void*
存在硬错误。
另请注意,无论如何使用C ++ 17时,可能更倾向于使用constexpr成员而不是enum。
template<typename T>
struct Test<T, typename A<T>::m>
{
static constexpr unsigned value = 1u;
};