这个 SFINAE C++语法是如何工作的

How does this SFINAE C++ syntax work?

本文关键字:工作 何工作 SFINAE C++ 语法 这个      更新时间:2023-10-16

我刚刚开始涉足SFINAE,我很难理解以各种形式出现的最常用示例背后的语法,但这个想法是检查特定类型是否包含给定的成员。这个特别的来自维基百科:

template <typename T> struct has_typedef_foobar 
{
    typedef char yes[1];
    typedef char no[2];
    template <typename C> static yes& test(typename C::foobar*);
    template <typename> static no& test(...);
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

几件事我不明白:

  1. 返回"yes"的 test() 重载的参数类型是什么?是指针吗?为什么将 typename 关键字用作参数的一部分?我已经看到它也用于测试一个类是否具有给定类型的成员,而不仅仅是 typedef,并且语法保持不变。
  2. 有时我看到使用test(int C::*)的例子。这更奇怪,不知道我们指的是C的哪个成员。如果它是一个带有主体的实函数,为实数类型实例化,并且参数被命名,它会指向什么以及如何使用它?
    模板 
        

    这些问题中的大多数与SFINAE无关:

    1. 当依赖名称应被视为类型时,它前面需要加上 typename 。由于C是要test()的模板参数,显然C::foobar是一个依赖名称。尽管函数声明要求在每个参数前面都有一个类型,但该语言需要使用 typename 将依赖名称C::foobar转换为类型。这样,typename C::foobar只是一个类型,*应用类型构造函数,就会生成相应的指针类型。
    2. int C::* 是指向类型为 int 的数据成员的未命名指针。
    3. 未使用的名称始终可以省略。这适用于函数参数以及模板参数,即,是的,如果不使用它,则可以省略template后面的名称。但是,大多数时候它以某种形式使用,在这种情况下,显然是必需的。
    4. 我认为你可以写一个测试来测试多个方面的存在,但我不会这样做:SFINAE 是不可读的。我宁愿显式地将不同的属性测试与普通逻辑运算符相结合。

SFINAE 的这个示例依赖于这样一个事实,即参数列表为 ... 的函数在执行重载解析时是最首选的。

所以首先,编译器将尝试

static yes& test(typename C::foobar*);

通过用C替换实际类型。如果C有一个名为 foobar 的成员类型,它将编译并选择。否则,它将编译失败,并且将选择...重载。它将始终编译。因此,要回答您的第一个问题,返回 yes& 的类型是具有成员类型的任何内容 foobar .

单词 typename 对于依赖类型是必需的:依赖于模板参数的类型。由于这些类型可以是变量名或类型名,因此编译器假定它是变量名,除非您使用 typename 告诉它。从理论上讲,它可以自行检查,但这会使编译器编写起来更加复杂,这显然是不可取的。

至于你的第二个问题,这是一个成员变量指针。您还可以使用成员函数指针,其完整形式实际上类似于 void test(int(C::*arg_name)())

至于三个,是的,它是允许的,但从不使用模板参数。由于您没有使用推导,而是显式指定参数,因此可以。就像像void f(int);这样的未命名的正常参数一样.

至于四个,是的,它可以,但就我所知的方式,你只需要为 n 个要测试的成员提供 n * 2 个函数。对于两个人来说,它看起来像

template <typename C> static yes& test1(typename C::foobar*);
template <typename> static no& test1(...);
template <typename C> static yes& test2(typename C::quux*);
template <typename> static no& test2(...);
static const bool value = sizeof(test1<T>(0)) + sizeof(test2<T>(0)) == sizeof(yes) * 2;
1) 在参数是其中一个模板参数的依赖

类型的模板中需要typename,在这种情况下C::foobar是参数C的依赖类型。test() 的参数类型是指向 C::foobar 的指针。如果 C::foobar 不是类型,则该版本的测试重载将无法编译,并且将找到另一个版本的重载。

UK4321涵盖了其余部分。