我刚刚开始涉足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);
};
我有几件事我不明白:
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?
答案 0 :(得分:4)
这些问题大多与SFINAE无关:
typename
开头。由于C
是test()
的模板参数,因此显然C::foobar
是从属名称。尽管函数声明需要在每个参数前面添加一个类型,但该语言需要使用typename
将依赖名称C::foobar
转换为类型。有了它,typename C::foobar
只是一个类型,并将类型构造函数*
应用于它,生成相应的指针类型。int C::*
是指向int
类型的数据成员的未命名指针。template
之后的名称可以省略。大多数情况下,它以某种形式使用,在这种情况下,显然是必需的。答案 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涵盖了其余部分。