我刚刚开始涉足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 回答
其中大多数问题与SFINAE无关:
当依赖名称应被视为类型时,它需要以
typename
开头 . 由于C
是test()
的模板参数,因此显然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不够可读 . 我宁愿将不同的属性测试与普通逻辑运算符结合使用 .
SFINAE的这个例子依赖于这样一个事实,即当进行重载解析时,参数列表为
...
的函数是最不受欢迎的 .首先,编译器将尝试
用
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个函数 . 对于两个,它看起来像
1)模板中需要
typename
,其中参数是模板参数之一的依赖类型,在这种情况下C::foobar
是参数C
的依赖类型 . test()的参数类型是指向C :: foobar的指针 . 如果C :: foobar不是一个类型,那么该版本的测试重载将无法编译,并且将找到另一个版本的重载 .其余的是uk4321 .