使用模板时可能是编译器中的 SFINAE 错误?

May be a SFINAE BUG in complier when use template?

本文关键字:编译器 SFINAE 错误      更新时间:2023-10-16

我想用标准代码来编写像 std::is_union 这样的实用程序,我们知道类类型不能扩展联合类型,这是错误,所以像这样的一些代码

#include <iostream>
template<typename T>
class class_type_can_extends :public T{
public:
using type = void;
};
template<typename T,typename U = void>
struct is_not_union:std::false_type {
};
template<typename T>
struct is_not_union < T, std::void_t<typename class_type_can_extends <T>::type >> :std::true_type {
};
class c_data{
};
union u_data{
};
int main(){
/*#1*/  std::cout<< is_not_union<c_data>::value<<std::endl; /*print true*/
/*#2*/  std::cout<< is_not_union<u_data>::value<<std::endl; /*this code make 
all complier error*/
}

G++ 打印错误:

main.cpp: In instantiation of ‘class class_type_can_extends<u_data>’:
main.cpp:26:43:   recursively required by substitution of ‘template<class T> struct is_not_union<T, std::void_t<typename class_type_can_extends<T>::type> > [with T = u_data]’
main.cpp:26:43:   required from here
main.cpp:3:7: error: base type ‘u_data’ fails to be a struct or class type
class class_type_can_extends :public T { 

叮当打印错误:

main.cpp:3:38: error: unions cannot be base classes
class class_type_can_extends :public T {
~~~~~~~^
main.cpp:14:47: note: in instantiation of template class 'class_type_can_extends<u_data>' requested here
struct is_not_union < T, std::void_t<typename class_type_can_extends <T>::type >> :std::true_type {
^
main.cpp:26:23: note: during template argument deduction for class template partial specialization 'is_not_union<T,
std::void_t<typename class_type_can_extends<T>::type> >' [with T = u_data]
/*#2*/  std::cout << is_not_union<u_data>::value << std::endl; /*this code make
^
main.cpp:26:23: note: in instantiation of template class 'is_not_union<u_data, void>' requested here
1 error generated.

与:

错误 C2569

为什么 #2 代码会让编译器出错,编译器会在 #2 代码上使用 SFINAE 规则(将 T 替换为"u_data",然后失败(,并选择主模板?为什么 sfinae 在这里无效,可能是这里的错误?

来自 cpp首选项:

只有函数类型或其模板参数类型或其显式说明符(自 C++20 以来(的直接上下文中的类型和表达式中的失败才是 SFINAE 错误。如果对替换类型/表达式的计算导致副作用,例如实例化某些模板专用化、生成隐式定义的成员函数等,则这些副作用中的错误将被视为硬错误

SFINAE 适用于即时上下文,这里您有一个硬错误失败。

typename class_type_can_extends<T>::type中,SFINAE 适用于不存在type,如果class_type_can_extends<T>实例化失败,则不适用。

请注意,我们不能仅使用标准C++来区分union和类类型 (不含std::is_union(。大多数编译器为此提供了内部函数。