【问题标题】:Overload -> operator to forward member-access through Proxy重载 -> 操作员通过代理转发成员访问
【发布时间】:2015-02-25 15:40:43
【问题描述】:

我正在尝试将 Python PyObject* 包装在 Object 类中。 在 Python 中,一切都是PyObject*。 一个列表是一个PyObject*,列表中的每个项目本身就是一个PyObject*。 这甚至可能是另一个列表。 等等

我正在尝试通过代理模式 (here) 来允许 fooList[42] = barObj 样式语法。

现在我已经完成了这项工作,我想扩展它,以便 fooList[42] 可以用作 Object。具体来说,我希望能够处理...

fooList[42].myObjMethod()
fooList[42].myObjMember = ...

Object有很多方法,目前fooList[42].myObjMethod()会先将fooList[42]解析成Proxy实例,比如tmpProxy,然后再尝试tmpProxy.myObjMethod()

这意味着我必须这样做

void Proxy::myObjMethod(){ return wrapped_ob.myObjMethod(); }

即通过Proxy手动中继Object的每个方法,这很难看。

我看不到任何完美的解决方案(请参阅上面的链接答案),但我很乐意使用:

fooList[42]->myObjMethod()

... 作为一种折衷方案,看到 -> 可以重载(而 . 不能)。

但是,我找不到任何有关重载 operator-> 的文档。

我最好的猜测是它必须返回一个指向某个对象的指针(比如pObj),而C++ 将调用pObj->whatever


以下是我尝试的实现。但是,我遇到了一个'获取 Object 类型的临时对象的地址'警告。

我有,在我的Object 类中:

const Object operator[] (const Object& key)     const { 
    return Object{ PyObject_GetItem( p, key.p ) }; 
}

注意'const Object&'会遇到'获取Object类型的临时对象的地址'警告。

class Proxy {
private:
    const Object& container;
    const Object& key;

public:
    // at this moment we don't know whether it is 'c[k] = x' or 'x = c[k]'
    Proxy( const Object& c, const Object& k ) : container{c}, key{k}
    { }

    // Rvalue
    // e.g. cout << myList[5] hits 'const Object operator[]'
    operator Object() const {
        return container[key];
    }

    // Lvalue
    // e.g. (something = ) myList[5] = foo
    const Proxy&  operator= (const Object& rhs_ob) {
        PyObject_SetItem( container.p, key.p, rhs_ob.p );
        return *this; // allow daisy-chaining a = b = c etc, that's why we return const Object&
    }

    const Object* operator->() const { return &container[key]; }
    // ^ ERROR: taking the address of a temporary object of type Object
};

这个想法是允许myList[5]-&gt;someMemberObj = ... 样式语法。

myList[5] 解析为Proxy 实例,它包装了ObjectmyList 的第六个元素)。我们就叫它myItem吧。

现在我希望someProxy-&gt;fooFunc()someProxy-&gt;fooProperty 分别调用myItem.fooFunc()myItem.fooProperty

我遇到了一个'获取 Object 类型的临时对象的地址'警告。

【问题讨论】:

标签: c++ c++11 operator-overloading proxy-pattern


【解决方案1】:

经过几个小时的哄骗大肠杆菌,我有一个工作测试用例。

请参考:https://codereview.stackexchange.com/questions/75237/c11-proxy-pattern-for-supporting-obidx-someobjmember-type-acc

非常感谢 Jarod,他为 -> 重载提供了正确的语法和理解。

【讨论】:

    【解决方案2】:

    我发现您作为示例编写的 Proxy 类有点令人困惑,所以我冒昧地对其进行了一些更改: 这是一个简单的例子:

    //object with lots of members:
    class my_obj
    {
    public:
        std::string     m_text;
    
        void foo()
        {
            std::cout << m_text << std::endl;
        }
        void bar(std::string t)
        {
            m_text = t;
        }
    };
    //proxy object
    class proxy_class
    {
    private:
        friend class CustomContainer;
        my_obj* px;
    
        proxy_class(my_obj * obj_px)
            :px(obj_px)
        {
        }
        proxy_class() = delete;
        proxy_class(const proxy_class &) = delete;
        proxy_class& operator =(const proxy_class &) = delete;
    public:
    
        my_obj* operator ->()
        {
            return px;
        }
    };
    //custom container that is the only one that can return proxy objects
    class CustomContainer
    {
    public:
        std::map<std::size_t, my_obj> stuff;
    
        proxy_class     operator [](const std::size_t index)
        {
            return proxy_class(&stuff[index]);
        }
    };
    

    示例用法:

    CustomContainer cc;
    cc[0]->foo();
    cc[0]->bar("hello world");
    cc[0]->foo();
    

    作为设计考虑,代理类应该在受控环境中创建,这样构造函数就可以避免误用。

    CustomContainer 必须只返回 proxy_class 并引用 my_obj 以便它可以使用任何东西,std::mapstd::vector

    【讨论】:

      【解决方案3】:

      如果您可以更改Object,您可以添加

      class Object {
      public:
          // other code
          const Object* operator -> () const { return this; }
          Object* operator -> () { return this; }
      };
      

      为了你的Proxy

      Object operator->() { return container[key]; }
      

      所以,例如

      myObj[42]->myFoo = ...
      

      基本上等同于

      Proxy proxy = myObj[42];
      Object obj = proxy.operator ->();
      Object* pobj = obj.operator ->(); // so pobj = &obj;
      pobj->myFoo = ...
      

      【讨论】:

      • 否则,您可以对第二种代理类型(按值存储对象)使用相同的技巧。
      • 感谢 Jarod42,您能解释一下为什么这样有效吗?例如,当例如遇到myObj[42]-&gt;myFoo = ...?我看不出是否会创建额外的临时对象。
      • @Pi: operator -&gt; 应该返回一些东西来应用-&gt;(可以使用链接,就像在当前的解决方案中一样)。已添加示例。
      猜你喜欢
      • 2016-09-08
      • 1970-01-01
      • 2011-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-05
      • 1970-01-01
      相关资源
      最近更新 更多