首页 文章

这个SFINAE C语法如何工作?

提问于
浏览
3

我刚刚开始涉足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);
};

有几件事我不明白:

  • 返回"yes"的test()重载的参数类型是什么?它是指针吗?为什么typename关键字用作参数的一部分?我已经看到它也用于测试一个类是否具有给定类型的成员,而不仅仅是一个typedef,并且语法保持不变 .

  • 有时我见过使用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?
  • 模板<typename>是否允许符号,如果它没有在第二次重载中使用?那它甚至是模板功能呢?

  • 额外奖励:是否可以一次检查是否存在多个成员?

3 回答

  • 4

    其中大多数问题与SFINAE无关:

    • 当依赖名称应被视为类型时,它需要以 typename 开头 . 由于 Ctest() 的模板参数,因此显然 C::foobar 是从属名称 . 即使函数声明需要每个参数前面的类型,该语言也需要使用 typename 将依赖名称 C::foobar 转换为类型 . 有了它, typename C::foobar 只是一个类型,并将类型构造函数 * 应用于它,生成相应的指针类型 .

    • int C::* 是指向 int 类型的数据成员的未命名指针 .

    • 总是可以省略未使用的名称 . 这适用于函数参数以及模板参数,即是,如果未使用,则可以省略 template 之后的名称 . 大多数情况下,它以某种形式使用,在这种情况下,显然是必需的 .

    • 我'd think you can write a test which tests for the presence of multiple aspects but I wouldn' t:SFINAE不够可读 . 我宁愿将不同的属性测试与普通逻辑运算符结合使用 .

  • 0

    SFINAE的这个例子依赖于这样一个事实,即当进行重载解析时,参数列表为 ... 的函数是最不受欢迎的 .

    首先,编译器将尝试

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

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

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

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

    至于三,是的,它是允许的,但模板参数从未使用过 . 既然你're not using deduction, but explicitly specifying the parameter, it'很好 . 就像一个未命名的正常参数,如 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;
    
  • 3

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

    其余的是uk4321 .

相关问题