5

这个问题在某种程度上是我发布的这个问题的延续。

我想做的是:我的意思是允许访问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;
}

对于允许我执行以下操作的事情 - 请注意,我即将继承该类,因为在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;
        }
    }
};

我想要的答案是:我真的很想让上面的代码工作——在适当的修改之后——但是我满意解释第一个代码块中做了什么——来自 litb 的解决方案。

4

4 回答 4

7

不幸的是,博客文章及其代码有点不清楚。这个概念很简单:显式模板实例化可以免费在后台传递给任何类,因为

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

分发此后台通行证的自然方式是作为指向成员的指针。如果您有一个指向给定类成员的指针,则可以在该类的任何对象中访问它,而不管访问资格如何。幸运的是,即使在 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;

用法:

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;

看到它的工作。

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

强大的版本。

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

template< typename type, type value, typename tag >
class access_bypass {
    friend type get( tag )
        { return value; }
};

用法:

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;

看到它的工作。

这个强大的版本和 litb 的博客文章之间的主要区别在于,我已将所有参数收集到一个地方并将标签结构设为空。它只是相同机制的更简洁的接口。但是您必须声明该get函数,博客代码会自动执行该函数。

于 2013-02-27T13:34:23.397 回答
4

好的,所以你问了如何让那个奇怪的“Rob”代码与你的用例一起工作,所以就是这样。

// the magic robber
template<typename Tag, typename Tag::type M>
struct Rob {
    friend typename Tag::type get(Tag) {
        return M;
    }
};

// the class you can't modify
class A {
private:
    std::map<int, int> A_map;
};

struct A_f {
    typedef std::map<int, int> A::*type;
    friend type get(A_f);
};

template struct Rob<A_f, &A::A_map>;

class B : private A {
public:
    inline void uncover() {
        std::map<int, int>::iterator it = (this->*get(A_f())).begin();
    }
};

现在,我个人认为这里的治疗方法可能比疾病更糟糕,尽管我通常是你看到的最后一个声称滥用 C++ 是可以的人。您可以自己决定,因此我将其发布为与我使用预处理器以老式方式完成的答案不同的答案。

编辑:

这个怎么运作

在这里,我将复制上面的代码,但简化了类型,并绘制了更多代码,并带有大量注释。请注意,在进行此练习之前,我对代码不是很了解,现在我还没有完全理解它,明天我当然不会记得它是如何工作的。警告维护者。

这是我们不允许更改的代码,使用私有成员:

// we can use any type of value, but int is simple
typedef int value_type;

// this structure holds value securely.  we think.
struct FortKnox {
    FortKnox() : value(0) {}
private:
    value_type value;
};

现在进行抢劫:

// define a type which is a pointer to the member we want to steal
typedef value_type FortKnox::* stolen_mem_ptr;

// this guy is sort of dumb, but he knows a backdoor in the standard
template<typename AccompliceTag, stolen_mem_ptr MemPtr>
struct Robber {
    friend stolen_mem_ptr steal(AccompliceTag) {
        return MemPtr; // the only reason we exist: to expose the goods
    }
};

// this guy doesn't know how to get the value, but he has a friend who does
struct Accomplice {
    friend stolen_mem_ptr steal(Accomplice);
};

// explicit instantiation ignores private access specifier on value
// we cannot create an object of this type, because the value is inaccessible
// but we can, thanks to the C++ standard, use this value in this specific way
template struct Robber<Accomplice, &FortKnox::value>;

// here we create something based on secure principles, but which is not secure
class FortKnoxFacade : private FortKnox {
public:
    value_type get_value() const {
        // prepare to steal the value
        // this theft can only be perpetrated by using an accomplice
        stolen_mem_ptr accessor = steal(Accomplice()); // it's over now
        // dereference the pointer-to-member, using this as the target
        return this->*accessor;
    }
};

int main() {
    FortKnoxFacade fort;
    return fort.get_value();
}
于 2013-02-27T11:35:23.603 回答
3

来点更残忍的怎么样?

// MY CODE -- module "b.h"
# define private protected
# include <a.h>
# undef private
class B : private A {
    // now you can access "private" members and methods in A
于 2013-02-27T11:12:08.850 回答
1

我所知道的这个成语的最佳打包版本如下:

template<class Tag,typename Tag::type MemberPtr>
struct access_cast{
 friend typename Tag::type get(Tag){return MemberPtr;};
};

template<class Tag,class MemberPtr>
struct access_tag{
 typedef MemberPtr type;
 friend type get(Tag);
};

class A {
public:
 auto x() const {return x_;};
private: 
 int x_ = 9;
};

#include <iostream>

struct AMemTag: access_tag<AMemTag,int A::*>{}; //declare tag
template struct access_cast<AMemTag,&A::x_>; //define friend get function

int main() {
 A a;
 std::cout<<a.x()<<"\n";
 a.*get(AMemTag()) = 4; //dereference returned member pointer and modify value
 std::cout<<a.x()<<"\n";
}

看到它的工作。

于 2015-04-24T19:19:35.780 回答