SFINAE 示例不起作用
SFINAE example not working
我正在努力弄清楚这个 SFINAE 概念,我必须说我觉得它真的很令人困惑。我理解为什么如果编译器可以根据模板 types/args 推断并选择正确的函数,那将是一个巨大的优势,但我不知道那是否是 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,但也不能落后太多。不用说了,两次显示"is not class":
#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();
}
这是什么原因造成的?能不能尽量少改,改成"work",也就是让test<Hejsa>(var1);
说"is not class",test<Hejsa>(var2)
说"is class"? ('Hejsa' 是一个丹麦语单词,意思是 'Hiya' 或 'Hello there',类似的意思;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
是 class 时(即使 class 没有类型 int
BTW 的成员),它的格式才正确。对于其他类型(如 float
),它的格式不正确。
...
是接受额外参数的省略号(如 printf
系列)。
所以当 C
是 class 时,两个函数都对 test<C>(0)
有效
yes& test(int C::* m); // with m = nullptr
no& test(...); // with ... taking int 0
超载解析规则做第一个被选中。 (所以 return 类型是 yes&
)。
但是当 C
不是 class(比如说 float
)
对于template<typename C> static yes& test(int C::*);
,用替换
我们会得到 yes& test(int float::*);
由于函数是模板,失败取决于模板,
我们没有出现错误,而是简单地忽略了重载集中的那个函数(它不是错误)。
只有省略号函数对 test<C>(0)
有效(因此 return 类型为 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"; }
std::enable_if_t<bool>
在 bool 为假时格式错误,否则由 void
替代。
根据您的代码,我会说您混淆了 SFINAE 和重载解析(可能是因为 cppreference.com 在示例中同时使用了两者)。
重载决议是编译器在选择要调用的函数时应用的规则。因此,它允许您根据传递的类型调用不同的函数。
SFINAE 将允许您测试是否可以对您不知道的类型进行操作。通常,测试类型是否具有给定的成员函数。
通常情况下,您无法编写测试它的代码,因为调用不存在的成员函数是错误的。但是,当应用重载决议规则时,编译器将丢弃一个会导致编译错误的函数。
编译成功,因为省略号重载是一种包罗万象。
现在,SFINAE 的兴趣在于,如果您的类型具有给定的成员函数,编译器将选择更精确的函数(省略号是最后的手段)。为函数使用不同的 return 类型,允许您检测选择了哪个重载。
我正在努力弄清楚这个 SFINAE 概念,我必须说我觉得它真的很令人困惑。我理解为什么如果编译器可以根据模板 types/args 推断并选择正确的函数,那将是一个巨大的优势,但我不知道那是否是 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,但也不能落后太多。不用说了,两次显示"is not class":
#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();
}
这是什么原因造成的?能不能尽量少改,改成"work",也就是让test<Hejsa>(var1);
说"is not class",test<Hejsa>(var2)
说"is class"? ('Hejsa' 是一个丹麦语单词,意思是 'Hiya' 或 'Hello there',类似的意思;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
是 class 时(即使 class 没有类型 int
BTW 的成员),它的格式才正确。对于其他类型(如 float
),它的格式不正确。
...
是接受额外参数的省略号(如 printf
系列)。
所以当 C
是 class 时,两个函数都对 test<C>(0)
yes& test(int C::* m); // with m = nullptr
no& test(...); // with ... taking int 0
超载解析规则做第一个被选中。 (所以 return 类型是 yes&
)。
但是当 C
不是 class(比如说 float
)
对于template<typename C> static yes& test(int C::*);
,用替换
我们会得到 yes& test(int float::*);
由于函数是模板,失败取决于模板, 我们没有出现错误,而是简单地忽略了重载集中的那个函数(它不是错误)。
只有省略号函数对 test<C>(0)
有效(因此 return 类型为 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"; }
std::enable_if_t<bool>
在 bool 为假时格式错误,否则由 void
替代。
根据您的代码,我会说您混淆了 SFINAE 和重载解析(可能是因为 cppreference.com 在示例中同时使用了两者)。
重载决议是编译器在选择要调用的函数时应用的规则。因此,它允许您根据传递的类型调用不同的函数。
SFINAE 将允许您测试是否可以对您不知道的类型进行操作。通常,测试类型是否具有给定的成员函数。
通常情况下,您无法编写测试它的代码,因为调用不存在的成员函数是错误的。但是,当应用重载决议规则时,编译器将丢弃一个会导致编译错误的函数。 编译成功,因为省略号重载是一种包罗万象。
现在,SFINAE 的兴趣在于,如果您的类型具有给定的成员函数,编译器将选择更精确的函数(省略号是最后的手段)。为函数使用不同的 return 类型,允许您检测选择了哪个重载。