C++ SFINAE示例不起作用

C++ SFINAE示例不起作用,c++,methods,sfinae,C++,Methods,Sfinae,我试图弄明白SFINAE的概念,我必须说我发现它真的很混乱。我理解,如果编译器能够根据模板类型/参数推断并选择正确的函数,这将是一个巨大的优势,但我不知道这是否是SFINAE,因为缩写词所代表的含义与此不同。也许你可以帮我解决这个问题,但现在这实际上不是我的问题 我的问题是:我在这里查找并尝试了一个SFINAE示例: 特别是告诉您模板类型C是对象类型还是内在类型(int、bool等)的模板。我所说的示例代码如下: template<typename T> class is_clas

我试图弄明白SFINAE的概念,我必须说我发现它真的很混乱。我理解,如果编译器能够根据模板类型/参数推断并选择正确的函数,这将是一个巨大的优势,但我不知道这是否是SFINAE,因为缩写词所代表的含义与此不同。也许你可以帮我解决这个问题,但现在这实际上不是我的问题

我的问题是:我在这里查找并尝试了一个SFINAE示例:

特别是告诉您模板类型C是对象类型还是内在类型(int、bool等)的模板。我所说的示例代码如下:

template<typename T>
class is_class {
    typedef char yes[1];
    typedef char no [2];
    template<typename C> static yes& test(int C::*); // selected if C is a     class type
    template<typename C> static no&  test(...);      // selected otherwise
public:
    static bool const value = sizeof(test<T>(0)) == sizeof(yes);
};
模板
课堂就是课堂{
typedef char yes[1];
typedef字符编号[2];
template static yes&test(int C::*);//如果C是类类型,则选择该选项
模板静态无测试(…);//否则选择
公众:
静态布尔常量值=sizeof(测试(0))==sizeof(是);
};
然后我想尝试一下,所以我对它进行了轻微的修改,没有改变任何可能会影响使用哪个函数的内容,最后得到了以下代码。我使用Visual Studio 2017。我认为它不会运行C++2017,但它不会落后太多。不用说,它两次表明“不是课堂”:

#包括
#定义say(x)printf(#x“\n”)
模板
空f(整数T::*){
printf(“f();\n”);
}
模板
空隙f(T){
printf(“正常f();\n”);
}
Hejsa类{
公众:
INTA;
Hejsa(){A=2;}
};
模板
课堂就是课堂{
公众:
typedef char yes[1];
typedef字符编号[2];
模板静态是&测试(int C::*){
说出(“is类”);返回新的“是”;
};//如果C是类类型,则选中该选项
模板静态编号和测试(…){
说(“不是阶级”);不;返回;
};//否则选择
公众:
静态布尔常量值=sizeof(测试(0))==sizeof(是);
};
int main(){
int var1=9;
Hejsa var2;
is_类::测试(var1);
is_类::测试(var2);
f(var1);
f(var2);
getchar();
}
这是什么原因造成的?您能否尽可能少地更改它并使其“工作”,即进行
test(var1)说“不是类”和
测试(var2)
说“是类”?(“Hejsa”是一个丹麦语单词,意思是“你好”或“你好”,类似的;P)

提前感谢,


托马斯

SFINAE是如何创造这种特质的:

首先,简单的typedef可以确保不同的大小

typedef char yes[1]; // sizeof == 1
typedef char no [2]; // sizeof != 1 (2 actually)
然后,2个重载函数(声明):

重载解析规则在选择第一个规则时执行。(因此返回类型为
yes&

但是当
C
不是一个类时(比如说a
float

对于
模板静态是&测试(int C::*),带有替换
我们将得到
yes&test(int float::*)

由于功能是模板,且故障取决于模板, 我们只需忽略重载集中的函数,而不是错误(它不是错误)

只有省略号函数对于
测试(0)
(因此返回类型为
否&

现在,使用
sizeof(test(0))
可以询问编译器将选择哪个重载(无需调用函数,因此不需要定义)

我们将最终结果存储在静态成员
is_class::value

一旦您有了一个traits,可能的用法包括重载中的标记分派或直接SFINAE:

template <typename T>
std::enable_if_t<is_class<T>::value> foo() { std::cout << "a class"; }

template <typename T>
std::enable_if_t<!is_class<T>::value> foo() { std::cout << "a class"; }
模板

std::enable_if_t foo(){std::cout foo(){std::coutSFINAE是如何创建这些特征的:

首先,简单的typedef可以确保不同的大小

typedef char yes[1]; // sizeof == 1
typedef char no [2]; // sizeof != 1 (2 actually)
然后,2个重载函数(声明):

重载解决规则在选择第一个规则时执行。(因此返回类型为
yes&

但是当
C
不是一个类时(比如说a
float

对于
模板静态是&测试(int C::*);
,使用替换 我们将得到
yes&test(int float::*);

由于功能是模板,且故障取决于模板, 我们只需忽略重载集中的函数,而不是错误(它不是错误)

只有省略号函数对于
测试(0)
(因此返回类型为
否&

现在,使用
sizeof(test(0))
可以询问编译器将选择哪个重载(无需调用函数,因此不需要定义)

我们将最终结果存储在静态成员
is_class::value

一旦您有了一个traits,可能的用法包括重载中的标记分派或直接SFINAE:

template <typename T>
std::enable_if_t<is_class<T>::value> foo() { std::cout << "a class"; }

template <typename T>
std::enable_if_t<!is_class<T>::value> foo() { std::cout << "a class"; }
模板

std::enable_如果_t foo(){std::cout foo(){std::cout从您的代码中,我会说您混淆了SFINAE和重载解析(可能是因为cppreference.com在示例中同时使用了这两者)

重载解析是编译器用于选择要调用的函数的规则。因此,它允许您根据传递的类型调用不同的函数

SFINAE允许您测试对未知类型的操作是否可行。通常,测试类型是否具有给定的成员函数

通常,您无法编写测试它的代码,因为调用不存在的成员函数是错误的。但是,当应用重载解析规则时,编译器将丢弃一个可能导致编译错误的函数。 编译成功是因为省略号重载是一种包罗万象的方法


现在,SFINAE的兴趣在于,如果您的类型具有给定的成员函数,编译器将选择更精确的函数(省略号是最后的选择)。使用不同的函数返回类型,可以检测选择了哪个重载。

从您的代码中,我想说您混淆了SFINAE和重载解析(可能是因为cppreference.com
template <typename T>
std::enable_if_t<is_class<T>::value> foo() { std::cout << "a class"; }

template <typename T>
std::enable_if_t<!is_class<T>::value> foo() { std::cout << "a class"; }