SFINAE 示例不起作用

SFINAE example not working

本文关键字:不起作用 SFINAE      更新时间:2023-10-16

我试图弄清楚这个SFINAE概念,我必须说我发现它真的很令人困惑。我理解为什么如果编译器可以根据模板类型/参数推断和选择正确的函数,那将是一个巨大的优势,但我不知道这是否是 SFINAE,因为首字母缩略词所代表的含义意味着否则 IMO。也许你可以为我解决这个问题,但现在这实际上不是我的问题。

我的问题是这样的:我从这里查找并尝试了一个 SFINAE 示例: https://en.cppreference.com/w/cpp/language/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);
};

然后我想尝试一下,所以我稍微修改了一下,没有改变任何可能影响将使用哪个函数的内容,最后得到了以下代码。我使用 Visual Studio 2017。我不认为它会C++2017年运行,但它不会落后太多。不用说,它两次显示"不是阶级":

#include<cstdio>
#define say(x) printf(#x "n")
template<typename T>
void f(int T::*) {
printf("f<T>();n");
}
template<class T>
void f(T) {
printf("normal f();n");
}
class Hejsa {
public:
int A;
Hejsa() { A = 2; }
};
template<typename T>
class is_class {
public:
typedef char yes[1];
typedef char no[2];
template<typename C> static yes& test(int C::*) {
say("is class"); return new yes;
}; // selected if C is a class type
template<typename C> static no&  test(...) {
say("is not class"); no _n; return _n;
};      // selected otherwise
public:
static bool const value = sizeof(test<T>(0)) == sizeof(yes);
};
int main() {
int var1 = 9;
Hejsa var2;
is_class<Hejsa>::test<Hejsa>(var1);
is_class<Hejsa>::test<Hejsa>(var2);
f(var1);
f(var2);
getchar();
}

是什么原因造成的?你能不能尽量少地改变它,让它"工作",即让test<Hejsa>(var1);说"不是阶级",test<Hejsa>(var2)说"是阶级"?("Hejsa"是一个丹麦词,意思是"Hiya"或"你好",类似于;P)

提前感谢,

托马斯

SFINAE 如何创建该特征:

首先,简单的Typedef,以确保不同的尺寸

typedef char yes[1]; // sizeof == 1
typedef char no [2]; // sizeof != 1 (2 actually)

然后,2 个重载函数(声明(:

template<typename C> static yes& test(int C::*);
template<typename C> static no&  test(...);

int C::*是类型int的成员上的指针。仅当C是一个类时,它才格式正确(即使该类没有 BTW 类型的int成员(。对于其他类型(如float(,它的格式不正确。

...是接受额外参数(如printf族(的省略号。

因此,当C是一个类时,这两个函数都对test<C>(0)

yes& test(int C::* m); // with m = nullptr
no&  test(...);        // with ... taking int 0

重载解析规则是否选择第一个。(所以返回类型是yes&(。

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

对于template<typename C> static yes& test(int C::*);,通过换人,我们会得到yes& test(int float::*);

由于函数是模板,故障取决于模板, 我们没有错误,而是简单地忽略重载集中的函数(这不是错误(。

只有省略号函数对test<C>(0)有效(因此返回类型为no&(。

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

我们将最终结果存储在静态杆件is_class::value中。

一旦你有一个特征,可能的用法包括在重载中或直接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"; }

当 bool 为假时,std::enable_if_t<bool>格式不正确,否则void替换。

从您的代码中,我会说您将 SFINAE 和重载分辨率混淆了(可能是因为 cppreference.com 示例中同时使用了两者(。

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

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

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

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