【问题标题】:Why this code allows accessing private variables为什么这段代码允许访问私有变量
【发布时间】:2018-04-10 16:15:26
【问题描述】:

几天前我遇到过这种方法(作为C++ course 中编程任务的解决方案之一)。

#include <iostream>

struct C {
    C(int i) { m_i = i; }
private:
    int m_i;
};

template<typename Tag, int C::* M>
struct Rob {
    friend
    int C::* get(Tag) {
        return M;
    }
};

template<typename D>
struct AcessorTag {
    typedef D C::*type;
};

template struct Rob<AcessorTag<int>, &C::m_i>;

int &get_c(C &cls) {
    return cls.*get(AcessorTag<int>());
}

int main()
{
    C c(13);
    std::cout << get_c(c);
    return 0;
}

你能解释一下为什么这段代码可以编译吗?这里发生的几乎所有事情都是 1) 我们将指向成员的指针作为参数传递给结构模板 2) 我们声明了一个友元函数,该函数只将该指针返回给成员。

这是标准的 C++ 吗? (我在 VS 2015 上测试过)

get 函数是struct Rob&lt;&gt; 的朋友,但不是struct C 的朋友。反正struct C好像没有朋友,怎么可以访问它的私人会员呢?

谢谢!

【问题讨论】:

标签: c++ templates private


【解决方案1】:

除了缺少 get 函数的前向声明之外,此代码应该可以正常工作并且符合标准。由于适用于模板的显式实例化定义的特殊规则,此代码有效:

17.8.2 显式实例化 [temp.explicit]

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

这么写

template struct Rob<AcessorTag<int>, &C::m_i>;

将绕过通常的访问检查,并为get 函数体提供指向其他不可访问成员的指针。

this answer 很好地描述了这种奇特规则的动机。

【讨论】:

    【解决方案2】:

    当然,你的代码不能用 gnu c++ 和 clang++ 编译;标识符get 不是C 的成员。即使您尝试实例化Rob,例如

    Rob<AcessorTag<int>, &C::m_i> rob;
    

    你得到了

    error: 'int C::m_i' is private.
    

    您可以随时询问 Microsoft 他们的 c++ 编译器是否符合任何标准。如果他们回答了,请告诉我他们的答案。

    【讨论】:

    • 编写 get 函数的前向声明是让 gcc 和 clang 对这段代码感到满意的全部内容。 VC++ 编译器关于这个问题的行为已经在 VS2017 中修复。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-17
    • 2017-01-10
    • 2013-05-11
    • 1970-01-01
    • 2019-01-06
    • 2018-09-23
    • 2015-01-03
    相关资源
    最近更新 更多