c++ 11:为什么可以在类外访问私有成员模板?

时间:2023-01-15 17:43:27

I just happened to find that a nested private template class can be accessed directly outside the enclosing class using a using directive:

我只是碰巧发现嵌套的私有模板类可以使用一个using指令直接在封闭类之外访问:

class wrapper
{
private:
    template <typename T>
    class __tklass {};

    class __klass {};
};

template <typename T>
using tklass = wrapper::__tklass<T>;    // Expected error but compiles OK

// using klass = wrapper::__klass;      // "Error: __klass is private"

int main()
{
    tklass<int> v1;                     // Expected error but compiles OK

    // wrapper::__tklass<int> v3;       // "Error: __tklass is private"
    // wrapper::__klass v4;             // "Error: __klass is private"
}

The lines marked "Error: __xxx is private" correctly report an error when uncommented. But the lines with tklass get compiled without any complaint from the compiler.

标记为“Error: __xxx是私有的”的行在未注释时正确地报告错误。但是与tklass相关的行会在编译时不会受到编译器的任何抱怨。

So why exactly doesn't the compiler flag tklass as error despite wrapper::__tklass being private? Is it by any chance allowed by the standard? If so, wouldn't that be considered a serious access violation?

那么为什么编译器不将tklass标记为错误,尽管包装器::__tklass是私有的?这个标准允许吗?如果是这样,这难道不被认为是严重的访问违规吗?

I tried this on gcc-4.9.2, clang-3.5.0 and visual studio 2013 express. GCC command line:

我在gcc-4.9.2、clang-3.5.0和visual studio 2013 express上尝试过。GCC命令行:

g++ -std=c++11 -pedantic -Wall -Wextra -Wshadow myfile.cpp

2 个解决方案

#1


23  

This is definitely a compiler bug, and actually one that has been known for quite some time: GCC #47346 (first reported in Jan 2011) and Clang #15914 (first reported May 2013). Your __tklass is clearly private, and the template alias is not marked friend, so this should be a simple access error.

这肯定是一个编译器错误,实际上已经知道了很长一段时间:GCC #47346(第一次报告是在2011年1月)和Clang #15914(第一次报告是在2013年5月)。显然,您的__tklass是私有的,并且模板别名没有标记为friend,因此这应该是一个简单的访问错误。

The simplest reproduction is from the Clang example attachment, this version compiles on both gcc 4.9.2 and clang 3.5.0, though should definitely compile on neither:

最简单的复制来自Clang示例附件,这个版本在gcc 4.9.2和Clang 3.5.0上编译,但是绝对不应该在以下两个版本上编译:

class A
{
  class B {};
};

template<typename>
using T = A::B;

T<void> t;

Clang is strictly better than GCC on this front however, as this particular bug seems to occur only with template aliases. A "workaround" (if you need such a thing for cases that the compiler allows incorrectly...) would be to revert back to pre-C++11 template aliasing:

但是,在这方面,Clang要比GCC好得多,因为这个bug似乎只出现在模板别名中。一种“变通方法”(如果您需要这样的东西来处理编译器允许不正确的情况……)是恢复到c++ 11模板前的别名:

template <typename>
struct T {
    using type = A::B;
};

T<void>::type t;

That code correctly fails to compile with clang (error: 'B' is a private member of 'A'), but still compiles fine with gcc.

正确的代码不能与clang一起编译(错误:“B”是“a”的私有成员),但是仍然可以对gcc进行编译。

#2


3  

Herb Sutter wrote long ago the article, how template member functions may provide a back-door into a class: http://www.gotw.ca/gotw/076.htm (pls. check the case 4: "The Language Lawyer", at the end)

Herb Sutter在很久以前就写了一篇文章,关于模板成员如何为类提供后门:http://www.gotw.ca/gotw/076.htm(请查看案例4:“语言律师”,最后)

It may give you the answer.

它可能会给你答案。

EDIT: I'm curious, what were the reasons for down-voting. I cite the article: "Is this a hole in C++'s access control mechanism, and therefore a hole in C++'s encapsulation? This demonstrates an interesting interaction between two C++ features: The access control model, and the template model. It turns out that member templates appear to implicitly "break encapsulation" in the sense that they effectively provide a portable way to bypass the class access control mechanism."

编辑:我很好奇,为什么要投反对票?我引用了这篇文章:“这是c++的访问控制机制中的一个漏洞,因此是c++封装中的一个漏洞吗?”这展示了两个c++特性之间有趣的交互:访问控制模型和模板模型。事实证明,成员模板似乎隐含地“中断封装”,因为它们有效地提供了一种绕过类访问控制机制的可移植方式。

Seems for me to be a reasonable answer. EDIT END

对我来说似乎是一个合理的答案。编辑结束

One could spent plenty of time trying to secure the interfaces by technical means private/protected, etc. My preferred way is to make an agreement among all developers to use well, understood rules complying with least surprise approach. (EDIT: And verify the code against these rules using the code-reviews/reg-exp scripts on regular basis)

一个人可以花大量的时间试图通过技术手段来保护接口,私有/受保护等等。我的首选方法是在所有开发人员之间达成协议,使用良好的、理解的、符合最少意外方法的规则。(编辑:并定期使用代码评审/正则-exp脚本根据这些规则验证代码)

"Don't try to find a technical solution for a social problem" B. Stroustrup

“不要试图为社会问题找到技术解决方案”B. Stroustrup

#1


23  

This is definitely a compiler bug, and actually one that has been known for quite some time: GCC #47346 (first reported in Jan 2011) and Clang #15914 (first reported May 2013). Your __tklass is clearly private, and the template alias is not marked friend, so this should be a simple access error.

这肯定是一个编译器错误,实际上已经知道了很长一段时间:GCC #47346(第一次报告是在2011年1月)和Clang #15914(第一次报告是在2013年5月)。显然,您的__tklass是私有的,并且模板别名没有标记为friend,因此这应该是一个简单的访问错误。

The simplest reproduction is from the Clang example attachment, this version compiles on both gcc 4.9.2 and clang 3.5.0, though should definitely compile on neither:

最简单的复制来自Clang示例附件,这个版本在gcc 4.9.2和Clang 3.5.0上编译,但是绝对不应该在以下两个版本上编译:

class A
{
  class B {};
};

template<typename>
using T = A::B;

T<void> t;

Clang is strictly better than GCC on this front however, as this particular bug seems to occur only with template aliases. A "workaround" (if you need such a thing for cases that the compiler allows incorrectly...) would be to revert back to pre-C++11 template aliasing:

但是,在这方面,Clang要比GCC好得多,因为这个bug似乎只出现在模板别名中。一种“变通方法”(如果您需要这样的东西来处理编译器允许不正确的情况……)是恢复到c++ 11模板前的别名:

template <typename>
struct T {
    using type = A::B;
};

T<void>::type t;

That code correctly fails to compile with clang (error: 'B' is a private member of 'A'), but still compiles fine with gcc.

正确的代码不能与clang一起编译(错误:“B”是“a”的私有成员),但是仍然可以对gcc进行编译。

#2


3  

Herb Sutter wrote long ago the article, how template member functions may provide a back-door into a class: http://www.gotw.ca/gotw/076.htm (pls. check the case 4: "The Language Lawyer", at the end)

Herb Sutter在很久以前就写了一篇文章,关于模板成员如何为类提供后门:http://www.gotw.ca/gotw/076.htm(请查看案例4:“语言律师”,最后)

It may give you the answer.

它可能会给你答案。

EDIT: I'm curious, what were the reasons for down-voting. I cite the article: "Is this a hole in C++'s access control mechanism, and therefore a hole in C++'s encapsulation? This demonstrates an interesting interaction between two C++ features: The access control model, and the template model. It turns out that member templates appear to implicitly "break encapsulation" in the sense that they effectively provide a portable way to bypass the class access control mechanism."

编辑:我很好奇,为什么要投反对票?我引用了这篇文章:“这是c++的访问控制机制中的一个漏洞,因此是c++封装中的一个漏洞吗?”这展示了两个c++特性之间有趣的交互:访问控制模型和模板模型。事实证明,成员模板似乎隐含地“中断封装”,因为它们有效地提供了一种绕过类访问控制机制的可移植方式。

Seems for me to be a reasonable answer. EDIT END

对我来说似乎是一个合理的答案。编辑结束

One could spent plenty of time trying to secure the interfaces by technical means private/protected, etc. My preferred way is to make an agreement among all developers to use well, understood rules complying with least surprise approach. (EDIT: And verify the code against these rules using the code-reviews/reg-exp scripts on regular basis)

一个人可以花大量的时间试图通过技术手段来保护接口,私有/受保护等等。我的首选方法是在所有开发人员之间达成协议,使用良好的、理解的、符合最少意外方法的规则。(编辑:并定期使用代码评审/正则-exp脚本根据这些规则验证代码)

"Don't try to find a technical solution for a social problem" B. Stroustrup

“不要试图为社会问题找到技术解决方案”B. Stroustrup