指向成员的指针是否绕过成员的访问级别?

Mat*_* M. 13 c++ standards-compliance c++11

我们臭名昭着的litb有一篇关于如何规避访问检查的有趣文章.

这个简单的代码充分证明了这一点:

#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;
}
Run Code Online (Sandbox Code Playgroud)

其中编译和运行(输出42)与GCC 4.3.4,GCC 4.5.1,GCC 4.7.0(见user1131467的评论),并与锵3.0和科莫ç编译/ C++ 4.3.10.1在C++ 03严格的模式和2005年的MSVC .

Luchian向我询问了这个答案,我用它来证明它实际上是合法的.我同意Luchian的说法很奇怪,但是Clang和Comeau都是最常用的"标准"编译器的竞争者(默认情况下比MSVC更多)......

我在标准草案中找不到任何内容(n3337是我得到的最后一个版本).

那么......任何人都可以证明它是合法的吗?

GMa*_*ckG 13

是的,这是合法的.相关文本见§14.7.2/ 12,讨论了显式模板实例化:

12通常的访问检查规则不适用于用于指定显式实例化的名称.[ 注意:特别是,函数声明符中使用的模板参数和名称(包括参数类型,返回类型和异常规范)可能是通常不可访问的私有类型或对象,模板可能是成员模板或成员函数通常无法访问.- 结束说明 ]

Emhpasis我的.

  • 啊! 这可能是我最喜欢标准的东西,要全面了解某些东西,你只需要跋涉整个事物并将各个部分拼凑起来. (4认同)

Jam*_*nze 5

代码显然是非法的(并且需要编译时诊断).在线:

template struct Rob<A_f, &A::a>;
Run Code Online (Sandbox Code Playgroud)

表达式A::a访问私有成员A.

标准非常明确:"访问控制统一应用于所有名称,无论名称是从声明还是表达式引用."(§11/ 4,重点补充).由于a是私人名称A,因此对它的任何引用A都是非法的.

  • 它实际上并不违法,稍后会为显式模板实例化添加一个例外."所有"显然具有误导性,它是"所有,除非另有说明".我不会投票,因为它非常不直观. (3认同)
  • 虽然你是正确的(IMO)在规范中指出这个矛盾,但这并不意味着"代码显然是非法的".这意味着,就像任何严重的法律术语一样,"如果这项法律的任何规则被证明是有缺陷的或非法的,那么它将被一个尽可能符合预期目的的规则所取代." (可分割性条款).你不能采取一个规则,并基于矛盾解释它与委员会的意图相矛盾. (2认同)