来自Johannes Schaub - litb的一篇博客文章
template<typename Tag, typename Tag::type M>
struct Rob {
friend typename Tag::type get(Tag) {
return M;
}
};
// use
struct A {
A(int a):a(a) { }
private:
int a;
};
// tag used to access A::a
struct A_f {
typedef int A::*type;
friend type get(A_f);
};
template struct Rob<A_f, &A::a>;
int main() {
A a(42);
std::cout << "proof: " << a.*get(A_f()) << std::endl;
}既然get函数没有在class A中定义,那么如何从a对象调用它呢?
编辑:
我不明白为什么get必须有标记作为参数,而不是a.*get<A_f>() => ok,这是由于ADL机制
发布于 2012-10-21 07:50:49
您不能从a调用get!实际上,get返回的是一个指向A内部成员的类指针,它的类型是int A::*,所以你需要一个A的实例来访问这个值。
举个例子,让我用一下你的代码:
struct A {
A(int a):a(a) { }
int b;
private:
int a;
};
void test() {
auto p = &A::b;
std::cout << a.*p << std::endl;
}我是从a内部调用p的吗?a没有p,这正是您的代码中发生的事情,get函数返回&A::a,而您使用a读取它的值!仅此而已,没有任何错误,我认为它将在所有编译器中编译。
这里还有一个问题:为什么C++允许使用A的私有成员声明模板。C++标准说:
14.7.2p8通常的访问检查规则不适用于用于指定显式实例化的名称。注意:具体地说,函数声明符中使用的模板参数和名称(包括参数类型、返回类型和异常规范)可以是私有类型或对象,它们通常是不可访问的,并且模板可以是通常不可访问的成员模板或成员函数。
但是如果你试图实例化或者甚至是typedef指定的模板,那么你就会得到一个错误。让我们稍微修改一下您的示例:
struct A {
private:
int a;
friend void f();
};
// Explicit instantiation - OK, no access checks
template struct Rob<A_f, &A::a>;
// Try to use the type in some way - get an error.
struct Rob<A_f, &A::a> r; // error
typedef struct Rob<A_f, &A::a> R; // error
void g(struct Rob<A_f, &A::a>); // error
// However, it's Ok inside a friend function.
void f() {
Rob<A_f, &A::a> r; // OK
typedef Rob<A_f, &A::a> R; // OK
}发布于 2012-10-21 06:40:51
这是合法的,因为朋友函数总是在全局作用域中,即使你在一个类中实现它们。换句话说,这就是:
class A
{
friend void go() {}
};只是一个快捷方式:
class A
{
friend void go();
};
void go() {}发布于 2012-10-21 12:08:34
这是gcc中的一个已知编译器错误,已在以后的版本中修复。请参见-:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41437
https://stackoverflow.com/questions/12993219
复制相似问题