假设我们有一些Foo
允许的对象:
cout << myFoo[3];
myFoo[5] = "bar";
这需要代理设计模式(斯科特·迈耶斯 (Scott Meyers) 详细介绍如下 https://stackoverflow.com/questions/26529354/does-c11s-r-value-reference-operator-obsolete-the-proxy-object-design)
但现在让我们假设每个myFoo[i]
也是一个Foo
实例。
myFoo[7] = Foo{...};
myFoo[5] = "bar"; // Foo has a Foo(std::string) non-explicit constructor
我即将实现,但我无法摆脱最后一个令人讨厌的“前向声明/不完整类型”错误。
首先,让我们先解决一个简单的问题:
// x = someConstObject[4], so this must be Rvalue access
// i.e. someConstObject[4] = ... would be a contradiction / const violation
const Object operator[] (const Object& key) const {
return Object{ PyObject_GetItem(p, key.p) };
}
这是基本的非递归代理模式:
Proxy operator [] ( const Object& key ) { return Proxy{ *this, key }; }
class Proxy {
private:
const Object& container;
const Object& key;
public:
// at this moment we don't know whether it is 'container[key] = x' or 'x = container[key]'
Proxy( const Object& c, const Object& k ) : container{c}, key{k}
{ }
// Rvalue
// e.g. cout << myList[5]
operator Object() const {
return container[key]; // <-- invokes the original const [] overload
}
// Lvalue
// e.g. myList[5] = foo
const Object& operator= (const Object& rhs_ob) {
PyObject_SetItem( container.p, key.p, rhs_ob.p );
return rhs_ob; // allow daisy-chaining a = b = c etc.
}
#if 0
// I think this should come for free, as the above Rvalue handler
// ... collapses a Proxy into an Object
// e.g. myList[5] = someOtherList[7]
const Proxy& operator= (const Proxy& rhs) {
// Force resolution of rhs into Object
PyObject_SetItem( pContainerObj->p, pKeyObject->p, static_cast<Object>(rhs).p /* rhs.value->p*/ );
return rhs;
}
#endif
// ^ Note: allows:
// e.g. x = y[1] = z[2]; // <-- y[1] must return an Object
// e.g. if( y[1] = z[2] ) // <-- assigns and then checks that y[1] evaluates to true
};
不确定我是否需要最后一个处理程序。
无论如何,为了使其递归,我们需要:
class Proxy : Object {
:
这意味着我们不能再定义Proxy
within Object
,否则我们会得到“尝试从不完整类型建立基数”编译器错误。
那么让我们这样做吧。如果可能的话,我们还必须修改构造函数来填充基类:
class Object::Proxy : public Object {
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]'
// If it's 'c[k] = x', setting the base class to c[k] is going to
// either set it to the old value of c[k]
// or a None object (if it didn't have any value previously)
// we had better be certain to make sure the original c[k] overload
// returns None if unsuccessful
Proxy( const Object& c, const Object& k )
: container{c}, key{k}, Object{c[k]} // <-- might fail!
{ }
然后,由于Object
基类,我们将不再需要手动处理类型转换为对象:
// Rvalue
// e.g. cout << myList[5] hits 'const Object operator[]'
#if 0
// it looks as though we don't need to do this given that
// we now have Object as base class
operator Object() const {
return container[key];
}
#endif
但这就是事情变得棘手的地方。
如果我们搬家Object::Proxy
的定义之外(实际上是之后)Object
, 原本的
Proxy operator [] ( const Object& key ) { return Proxy{ *this, key }; }
...现在给我们一个错误,因为我们使用了一个不完整的类(Proxy
)。请注意,简单地将定义移到外部并不能解决返回类型为Proxy
。如果只是这样的话Proxy*
我们可以做到。但Proxy
cannot.
这似乎是第 22 条军规,我看不到任何干净的解决方案。
有吗?
编辑:为了回应暗示设计有缺陷的评论,请记住Object
是一个指针的轻量级包装。它只有一个PyObject*
数据成员。
编辑:我正在工作的原始代码被发现here http://sourceforge.net/p/cxx/code/HEAD/tree/trunk/CXX/CXX/Python3/Objects.hxx#l1084