我刚开始涉猎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);
};
对于这个问题,我有几点不理解:
- 返回"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); // 类似于这样? func(&foo::x); // 或者甚至像这样? - 如果在第二个重载中未使用,则是否允许使用没有符号的template <typename>?那么它怎么成为一个模板函数呢?
- 额外加分:它能检查多个成员的存在吗?
0
,因此它是一个空指针。唯一重要的是,成员指针声明是否有效。 - XeoC
实例化该函数并传递指向现有实例的指针,那么参数int C :: * arg
是什么意思?它是否合法? - neuviemeporteC
有两个int
成员,比如x
和y
,并且有一个函数可以处理其中任意一个。你可以将&C::x
或&C::y
传递给该函数以指定使用哪一个。这些的类型将是int (C::*arg)
(我认为在给成员指针命名时需要括号),你可以使用this->*arg
来访问相应的成员。这些家伙被称为_成员指针_。 - Dietmar KühlC
, 但是没有一个名为C::
的成员。 - neuviemeporteint C::*
是一个指向成员的指针:你可以像其他指针一样随意命名。在这方面,它就像任何其他指针一样。然而,成员指针并不是真正的指针,因为它们不指向内存中的地址。相反,它们更像是一个偏移量,指定如何访问给定类的特定成员,给定该类的对象。您可以使用template <typename T> void f(T& obj, int T::*mem) { obj.*mem = 17; }
与您的类(假设成员是public
)一起使用,例如Foo foo; f(foo, &Foo::x);
。 - Dietmar Kühl