这个SFINAE C ++语法如何工作?

时间:2013-11-09 23:28:26

标签: c++ templates sfinae

我刚刚开始涉足SFINAE并且我无法理解以各种形式出现的最常用示例背后的语法,但其目的是检查特定类型是否包含给定成员。这个特殊的来自Wikipedia

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成员。如果它是一个真实的函数与一个实体,实例化为一个真实的类型,并且参数被命名,它会指向什么,你将如何使用它?
    template <typename T> func(int T::*arg)
    {
        *arg = 1;
    }
    
    struct Foo
    {
        int x;
    } foo;
    
    func<Foo>(&foo::x); // something like this?
    func(&foo::x); // or maybe even like this?
    
  3. 是模板&lt; typename&gt;如果没有在第二次过载中使用,则不允许使用符号?那么它甚至是模板功能呢?
  4. 奖励:是否可以一次检查是否存在多个成员?

3 个答案:

答案 0 :(得分:4)

这些问题大多与SFINAE无关:

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

答案 1 :(得分:3)

SFINAE的这个例子依赖于这样一个事实:当进行重载解析时,参数列表为...的函数是 least 首选。

首先,编译器将尝试

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

C代替真实类型。如果C具有名为foobar的成员类型,则它将被编译并被选中。如果没有,它将无法编译,并且将选择...重载。它总是会编译。因此,要回答您的第一个问题,返回yes&的类型是具有成员类型foobar的任何类型。

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

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

至于三,是的,它是允许的,但模板参数从未使用过。由于您没有使用演绎,而是明确指定参数,因此没问题。就像一个未命名的正常参数,如void f(int);

对于四,是的它可以,但是就我所知的方式而言,你只需拥有 n <2> n 成员函数,你想要测试它们对于。对于两个,它看起来像

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;

答案 2 :(得分:0)

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

uk4321涵盖了其余部分。