SFINAE示例不起作用

时间:2018-06-16 19:31:21

标签: c++ methods sfinae

我试图弄清楚这个SFINAE概念,我必须说我觉得它真的很混乱。我理解为什么如果编译器可以根据模板类型/ args推断并选择正确的函数,这将是一个巨大的优势,但我不知道那是不是SFINAE,因为缩写代表的含义是什么意思否则IMO。也许你可以为我排序,但现在这不是我的问题。

我的问题是这样的:我抬起头来试试SFINAE的例子: https://en.cppreference.com/w/cpp/language/sfinae

特别是告诉您模板类型C是对象类型还是内在类型(int,bool等)的那个。我所谈论的示例代码是:

[['F', 0, 0, 0, 1],
 ['T', 0, 3, 0, 4],
 ['B', 1, 1, 0, 1],
 ['K', 2, 2, 0, 1],
 ['J', 2, 4, 0, 2],
 ['Bxx', 4, 4, 0, 1]]

然后我想尝试一下,所以我稍微修改了它,改变了可能影响使用哪个函数的任何东西,最后得到了以下代码。我使用Visual Studio 2017.我不认为它运行C ++ 2017,但它不会落后。毋庸置疑,它表明"不是阶级"两次:

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);
};

造成这种情况的原因是什么?你能不能尽可能少地改变它并使它工作&#34;也就是说,使#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(); } 说&#34;不是等级&#34;并且test<Hejsa>(var1);说&#34;是班级&#34;? (&#39; Hejsa&#39;是一个丹麦语,意思是“Hiya&#39;或者#39; Hello there&#39;,类似的东西; P)

提前致谢,

托马斯

2 个答案:

答案 0 :(得分:1)

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是一个类时,它才能很好地形成(即使该类没有类型为int BTW的成员)。对于其他类型(如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为false时,

std::enable_if_t<bool>生成错误,否则被void替换。

答案 1 :(得分:1)

从您的代码中,我会说您混淆了SFINAE和重载决策(可能因为cppreference.com在示例中使用了两者)。

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

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

通常情况下,您无法编写测试代码,因为调用不存在的成员函数是错误的。但是,在应用重载决策规则时,编译器将丢弃一个会导致编译错误的函数。 编译成功,因为省略号重载是一种全部捕获。

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