3

我刚刚开始涉足 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. 返回“是”的 test() 重载的参数类型是什么?是指针吗?为什么 typename 关键字用作参数的一部分?我已经看到它还用于测试一个类是否具有给定类型的成员,而不仅仅是 typedef,并且语法保持不变。
  2. 有时我会看到使用 test(int C::*) 的示例。这更奇怪,不知道我们引用的是 C 的哪个成员。如果它是一个带有主体的真实函数,为真实类型实例化,并且参数被命名,它会指向什么以及如何使用它?
    模板 <typename T> func(int T::*arg)
    {
        *arg = 1;
    }
    
    结构 Foo
    {
        诠释 x;
    } 富;
    
    func<Foo>(&foo::x); // 像这样?
    函数(&foo::x); // 或者甚至可能像这样?
    
  3. 如果没有在第二次重载中使用模板 <typename> 是否允许使用没有符号?那么它甚至是模板函数吗?
  4. 奖励:可以一次检查多个成员的存在吗?
4

3 回答 3

5

这些问题大多与 SFINAE 无关:

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

这个 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;
于 2013-11-09T23:33:57.907 回答
0

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

uk4321 涵盖了其余部分。

于 2013-11-09T23:37:46.597 回答