我们臭名昭着的litb在how to circumvent the access check上有一篇有趣的文章。
这个简单的代码充分证明了这一点:
#include <iostream>
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;
}
使用gcc 4.3.4,gcc 4.5.1,gcc 4.7.0编译并运行(输出42
)(请参阅user1131467&#39;评论)并使用Clang 3.0和Comeau C /进行编译C ++ 4.3.10.1在 C ++ 03严格模式和MSVC 2005中。
Luchian在this answer上问过我,我用它来证明它实际上是合法的。我同意Luchian的观点,这很奇怪,但是Clang和Comeau都是最具竞争力的竞争对手#34; Standard&#34;编译器可用(默认情况下比MSVC更多)......
我在标准草案中找不到任何内容(n3337是我得到的最后一个版本)。
那么......任何人都能证明它是合法还是不?
答案 0 :(得分:13)
是的,这是合法的。相关文本见§14.7.2/ 12,讨论了显式模板实例化:
12通常的访问检查规则不适用于用于指定显式实例化的名称。 [注意:特别是,函数声明符中使用的模板参数和名称(包括参数类型,返回类型和异常规范)可能是通常无法访问的私有类型或对象,模板可能是一个成员模板或成员函数,通常不会 无障碍。 - 结束记录]
Emhpasis mine。
答案 1 :(得分:5)
代码显然是非法的(并且需要编译时诊断)。 在该行:
template struct Rob<A_f, &A::a>;
表达式A::a
访问A
的私有成员。
标准非常明确:“应用访问控制
统一到所有名称,无论是否引用名称
声明或表达。“(§11/ 4,重点补充)。由于a
是A
中的私人名称,因此在A
之外对其进行的任何引用都是非法的。