允许访问私人会员

Rub*_*ens 5 c++ inheritance templates private class

这个问题是有点的延续这一个我已经张贴.

我试图做的是:我的观点是允许访问A派生类中基类的私有成员B,具有以下限制:

  • 我想要访问的是一个结构 - std::map<>实际上 - ,而不是一个方法;
  • 无法修改基类;
  • 基类A没有模板化方法我可能会作为后门替代方法重载 - 我不会添加这样的方法,因为它会违反第二个约束.

作为一种可能的解决方案,我已经指出了litb的解决方案(帖子/博客),但是,对于我的生活,我无法就这些帖子中的内容达成一致,因此,我可以没有得到我的问题的解决方案.

我要做的是:以下代码,来自litb的解决方案,介绍了如何从类/结构访问私有成员的方法,它恰好涵盖了我提到的限制.

所以,我正在尝试重新排列这一个代码:

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)

对于允许我执行以下操作的事情 - 请注意我即将继承该类,因为std::map<>在派生类初始化之后添加了条目B,即,std::map<>它不仅仅是类的静态成员A使用默认值,所以我需要从以下特定实例访问它B:

// NOT MY CODE -- library <a.h>
class A {
private:
    std::map<int, int> A_map;
};

// MY CODE -- module "b.h"
# include <a.h>
class B : private A {
public:
    inline void uncover() {
        for (auto it(A_map.begin()); it != A_map.end(); ++it) {
            std::cout << it->first << " - " << it->second << std::endl;
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

我想要的答案是:我真的很想让上面的代码工作 - 经过适当的修改 - 但我对第一个代码块中做了什么的解释非常满意 -来自litb的解决方案.

Pot*_*ter 6

不幸的是,博客文章及其代码有点不清楚.概念很简单:显式模板实例化获得任意类的免费后台传递,因为

  • 库类的显式实例化可以是客户端类的实现细节,和
  • 显式实例化只能在命名空间范围内声明.

分发此后台传递的自然方式是指向成员的指针.如果您有指向给定类成员的指针,则无论访问限制如何,都可以在该类的任何对象中访问它.幸运的是,即使在C++ 03中,指向成员的指针也可以是编译时常量.

因此,我们需要一个在显式实例化时生成指向成员的指针的类.

显式实例化只是定义类的一种方式.如何只是生成一个类什么?有两种选择:

  • 定义一个friend不是该类成员的函数.这就是litb所做的.
  • 定义静态数据成员,该成员在启动时初始化.这是我的风格.

我首先介绍我的风格,然后讨论它的缺点,然后修改它以匹配litb的机制.最终结果仍然比博客中的代码更简单.

简单版.

该类有三个模板参数:受限成员的类型,它的实际名称,以及对接收指向它的指针的全局变量的引用.该类调度要初始化的静态对象,其构造函数初始化全局.

template< typename type, type value, type & receiver >
class access_bypass {
    static struct mover {
        mover()
            { receiver = value; }
    } m;
};

template< typename type, type value, type & receiver >
typename access_bypass< type, value, receiver >::mover
    access_bypass< type, value, receiver >::m;
Run Code Online (Sandbox Code Playgroud)

用法:

type_of_private_member target::* backstage_pass;
template class access_bypass <
    type_of_private_member target::*,
    & target::member_name,
    backstage_pass
>;

target t;
t.* backstage_pass = blah;
Run Code Online (Sandbox Code Playgroud)

看得出来了.

不幸的是,在程序进入之前,你不能依赖于其他源文件中的全局对象构造函数可用的结果main,因为没有标准的方法告诉编译器初始化文件的顺序.但是整数是按顺序初始化的它们已被声明,因此您可以将旁路放在顶部,只要静态对象构造函数不对其他文件进行函数调用,您就可以了.

强大的版本.

这通过添加标签结构和friend函数从litb的代码中借用了一个元素,但这是一个小修改,我认为它仍然非常清楚,并不比上面更糟糕.

template< typename type, type value, typename tag >
class access_bypass {
    friend type get( tag )
        { return value; }
};
Run Code Online (Sandbox Code Playgroud)

用法:

struct backstage_pass {}; // now this is a dummy structure, not an object!
type_of_private_member target::* get( backstage_pass ); // declare fn to call

// Explicitly instantiating the class generates the fn declared above.
template class access_bypass <
    type_of_private_member target::*,
    & target::member_name,
    backstage_pass
>;

target t;
t.* get( backstage_pass() ) = blah;
Run Code Online (Sandbox Code Playgroud)

看得出来了.

这个强大的版本和litb的博客文章之间的主要区别在于我已将所有参数收集到一个地方并使标记结构为空.它只是一个相同机制的更清晰的接口.但是你必须声明get博客代码自动执行的功能.