为什么SFINAE在这种情况下不起作用?
Why does SFINAE not work in such a case?
#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*&>
没事,让我惊讶的是,Test<void*>
应该没事!
为什么SFINAE在后一种情况下不起作用?
您的第二种情况是硬错误。
SFINAE @ cppreference.com
说:
只有函数类型或其模板参数类型的直接上下文中的类型和表达式中的故障才是 SFINAE 错误。如果对替换类型/表达式的计算导致副作用,例如实例化某些模板专用化、生成隐式定义的成员函数等,则这些副作用中的错误将被视为硬错误。
第一种情况是可以的,因为如果T
void*&
remove_pointer
则不起作用,则由于引用折叠而m
void*&
,并且对指向 void 的指针的引用是有效的类型,而对 void 的引用则不是。
第一种情况下的类型仍然Test<void*&, void>
不Test<void*&, void*&>
,因为您只指定第一个模板参数。
第二种情况失败但不是第一种情况的原因是编译器必须实例化专用模板,因为第二个参数是非推导上下文,因此编译器无法立即判断专用化是否是更好的匹配。但在第二种情况下,实例化会产生硬错误,而在第一种情况下则不会。
主模板仍处于选中状态,因为它是更好的匹配项(同时仍会实例化专用模板以检查其是否匹配)。
注意:我不能说专用化是否实际上是完全实例化的,或者编译器是否只是查找typename A<T>::m
以检查这种专用化是否更适合。然而,结果是一样的。如果void*
存在硬错误。
另请注意,无论如何,当使用 C++17 时,人们可能更愿意使用 constexpr 成员而不是枚举。
template<typename T>
struct Test<T, typename A<T>::m>
{
static constexpr unsigned value = 1u;
};
- G++ C++17 类模板参数推导在非常特殊的情况下不起作用
- 为什么在这种情况下,前向声明不起作用?
- 为什么 std::void_t 在这种情况下不起作用
- 为什么在这种情况下 boost::recursive_wrapper 不起作用
- 为什么在这种情况下创建临时实例不起作用?
- 为什么SFINAE在这种情况下不起作用?
- 为什么在这种情况下转发引用不起作用?
- 在这种情况下,过载不起作用
- 为什么模板论点不能在这种情况下推导
- 请帮助我理解为什么SFINAE在这种情况下不起作用
- 为什么#ifndef在这种情况下不起作用
- 为什么c_str在这种打开(文件名)的情况下不起作用
- 在这种情况下__declspec( align( # ) ) 不起作用
- 为什么cvPutText在这种情况下不起作用?
- 在这种情况下,最小和最大根本不起作用?
- 内联扩展可能不起作用的情况
- 为什么贪婪的方法在这种情况下不起作用?
- 为什么 std::function<boost::any ()> 在这种情况下不起作用?
- 为什么在这种情况下"自动"不起作用?
- 为什么多态性在这种情况下不起作用