为什么通常的访问控制检查适用于通过模板参数访问时用于指定显式实例化的名称?

Com*_*czy 3 c++ gcc templates

C++标准在注释14.7.2/12 [temp.explicit]中声明了以下内容:

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

如果我可以实例化它,我希望可以使用模板.

我尝试使用gcc-4.8.2,当我访问显式命名类的私有成员时,我得到了预期的行为.但是,当我通过模板参数访问私有成员时,访问检查规则确实适用.这是gcc中的错误,还是我错过了什么?

在下面的代码中,'succeeded'和'failed'之间的唯一区别是前者通过'A'直接访问私有成员,而后者通过模板参数'T'访问它.编译器抱怨privateFoobar在该上下文中是私有的.

#include <iostream>
#include <string>

struct A
{
private:
    std::string privateFoobar() {return "private foobar!";}
};

typedef std::string (A::*Foobar)();

template <class Type, Type value>
struct Access
{
    static Type getValue() {return value;}
};

template <class T>
struct IndirectAccess
{
    static Foobar succeeds() {return Access<Foobar, &A::privateFoobar>::getValue();}
    static Foobar fails() {return Access<Foobar, &T::privateFoobar>::getValue();}
};

template class Access<Foobar, &A::privateFoobar>;

int main() {
    std::cout << (A().*Access<Foobar,&A::privateFoobar>::getValue())() << std::endl;
    std::cout << (A().*IndirectAccess<A>::succeeds())() << std::endl;
    std::cout << (A().*IndirectAccess<A>::fails())() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

如果您想知道这种可解除的攻击的用例是什么:创建一个框架,该框架将根据所选组件的实现选择自动配置应用程序.

Jon*_*ely 5

显式实例化必须在命名空间范围内,这意味着通常不能访问类的私有成员.没有你引用的规则,这是不可能的:

class Foo
{
private:
  struct Bar;

  template<typename T> class Baz { };

public:
  void f();  // does things with Baz<Bar>
};

// explicit instantiation declaration
extern template class Foo::Baz<Foo::Bar>;
Run Code Online (Sandbox Code Playgroud)

如果没有这个规则,我将无法命名Foo::Bar甚至Foo::Baz命名空间,因为这些名称是私有的Foo.

由于我没有实际使用 Foo::BarFoo::Baz在这里,只是提到他们的名字,告诉我实例化模板其他地方的编译器,没有真正的访问冲突(尽管它可能使用这个规则来执行一个非常狡猾的伎俩不否则可能).

类似地,当我在其他文件中编写显式实例化定义时,我需要能够在命名空间范围内再次引用私有名称.

  • 我认为[this](http://stackoverflow.com/a/1044437/464581)是Johannes在SO上的首次阐述。在那里,他还举例说明了成员函数指针转换规则中的违反直觉的类型系统漏洞。 (2认同)