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

nav*_*nav 33 c++ templates using language-lawyer c++11

我碰巧发现可以使用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"
}
Run Code Online (Sandbox Code Playgroud)

标记为"错误:__ xxxx为私有"的行在取消注释时正确报告错误.但是tklass编译的行没有编译器的任何抱怨.

那么,为什么编译器标记tklass为错误,尽管wrapper::__tklass它是私有的?是否有标准允许的任何机会?如果是这样,那不会被认为是严重的访问违规行为吗?

我试过gcc-4.9.2,clang-3.5.0和visual studio 2013 express.GCC命令行:

g++ -std=c++11 -pedantic -Wall -Wextra -Wshadow myfile.cpp
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 26

这绝对是一个编译器错误,实际上已经知道了很长一段时间:GCC#47346(2011年1月首次报道)和Clang#15914(2013年5月首次报道).你__tklass显然private,和模板别名没有标记friend,所以这应该是一个简单的访问错误.

最简单的再现来自Clang示例附件,此版本在gcc 4.9.2和clang 3.5.0上编译,但绝对不应该编译:

class A
{
  class B {};
};

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

T<void> t;
Run Code Online (Sandbox Code Playgroud)

但是,Clang在这方面严格优于GCC,因为这个特殊的bug似乎只出现在模板别名中.一个"解决方法"(如果你需要这样的东西,编译器允许错误的情况......)将恢复到预C++ 11模板别名:

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

T<void>::type t;
Run Code Online (Sandbox Code Playgroud)

该代码正确无法使用clang进行编译(错误:'B'是'A'的私有成员),但仍可使用gcc编译.


Val*_*itz 3

Herb Sutter 很早之前写过一篇文章,模板成员函数如何为类提供后门: http://www.gotw.ca/gotw/076.htm(请检查案例 4:“语言律师” , 在最后)

它可能会给你答案。

编辑:我很好奇,拒绝投票的原因是什么。我引用了这篇文章: “这是 C++ 访问控制机制中的一个漏洞,因此也是 C++ 封装中的一个漏洞吗?这演示了两个 C++ 功能之间有趣的交互:访问控制模型和模板模型。事实证明,成员模板似乎隐含地“破坏封装”,因为它们有效地提供了一种绕过类访问控制机制的可移植方法。”

在我看来这是一个合理的答案。 编辑结束

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

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

  • 链接的文章讨论了通过专门化模板成员来破坏访问控制 - 它不适用于这个问题。该问题的问题在于,在别名模板内引用时,访问控制不适用于名称“__wrapper::__tklass”。[引用的名称本身恰好是一个模板这一事实实际上是无关的。](http://coliru.stacked-crooked.com/a/dced7a56390facaa) (10认同)
  • 不用担心。由于我不太清楚的原因,C++ 碰巧吸引了从宗教角度思考它的狂热者。任何(或简单地被视为)“不完美”的事物都被理解为亵渎。我的猜测是,在现实世界中编写大量真实代码后,这种关于什么是体面但非常不完美且逻辑上有缺陷的语言的荒谬观点将会自我治愈。 (5认同)
  • 否决票是因为答案仅是链接,并且链接的项目是关于不同的问题并且没有回答问题。 (4认同)
  • 好吧,我只是投了反对票,因为那个洞与OP所说的不一样。更重要的是,您的答案的内容(您声称是问题的内容)仅包含在链接的文档中,而不是在这里:这使其成为一个糟糕的答案(链接的目标将来可能会发生变化,从而呈现答案)甚至不太有用),以及不正确(链接的答案没有回答OP的问题)。这是投反对票的两个原因。否决投票的第三个原因是用“**编辑**:为什么我被否决”来回应否决投票。4、回答格式不当。 (3认同)