My concrete problem is this (it is a bit special, but there may be other
instances of it):
I'd like to create a class for vectors (say: Vector), exporting math. methods, say v.foo(...).
I'd like to implement 'operator()' s.t. I can treat parts of a vector in the same
way as a normal vector: if v.foo(...) works, so should v(rng).foo(...), where
rng is a range object. This should work even if 'foo' is a non-const method.
I do this by letting Vector objects be normal, or be "masks". Masks refer to normal Vector objects and also maintain a range.
Vector has a copy constructor (CC), which (to ensure a safe handling) ALWAYS
draws a copy of its const Vector& argument, the new object will be normal.
The same safety restriction holds for 'operator=' of Vector. So:
Vector newv(v(rng));
newv=v2(rng2);
newv will be a new normal vector, containing a copy of v(rng), and the
assignment copies v2(rng2) to newv. v(rng) and v2(rng2) are masks, but
newv is always normal.
I could implement 'operator()' having return type 'Vector', returning a mask,
but then s.th. like
v(rng).foo(...);
would lead to a temp. copy of mask -> normal Vector been done, using the CC.
I don't want that for efficiency, but I also do not want to modify the CC to
create masks from masks (would be unsafe in my context).
I tried the following: I created a wrapper class VectorWrap, containing a
member Vector* vec. I added a special CC to Vector:
Vector(const VectorWrap& vw) {
// Take all resources of *vw.vec and make the new vector use them.
// Remove all resources from *vw.vec.
// With "resources", I mean members, such as vector buffer for a normal
// vector, or range for a mask
}
The destructor of VectorWrap destroys *vec, which frees resources of *vec
IF it has any. In my application, it is OK for a Vector object not to have
resources. It is the empty vector then.
Finally:
Vector Vector::operator()(const Range& rng) {
Vecttor* vec;
// Allocate vec as mask to *this with range rng
return VectorWrap(vec);
}
In an expression v(rng), a mask is created and wrapped. The return value
of 'operator()' has different type from its return type, so the special Vector
CC is used, avoiding copying. The temp. VectorWrap object is destroyed,
but this is OK, because at that time, the special Vector CC has already
removed the resources of vec. Fine.
Now my problem: This works fine if v(rng) is used as rvalue in an expression,
say s.th. like w+=v(rng) (if 'operator+=' is given for Vector, with
const Vector& argument.
But it does NOT work if v(rng) is to be used as lvalue. S.th. like
v(rng).foo(...);
is rejected by the compiler. I see why this "rvalue restriction" is in place
in general. v(rng) is temporary, so if 'foo' is not a method declared const,
this should not be allowed in general. The golden rule seems to be: do not
do anything not declared const on a temp. created object.
But in my case, it is safe and makes perfect sense, to allow things like
v(rng)+=w
Add vector w to subvector of v, thereby really modifying v. This is done
via a temp. object, and of course I cannot declare 'operator+=' to be const,
because then
v+=w
would not work.
If you know a simple way of getting around this "rvalue restriction", I'd
be very grateful!
Some "solutions" I considered:
- What I am doing right now: Since I know that vec(rng) should be used as
lvalue, I do not use the special CC above, but:
Vector* VectorWrap::operator->() const {
return vec;
}
VectorWrap::operator Vector&() {
return *vec;
}
Then, vec(rng).foo(...) can at least be done as
vec(rng)->foo(...)
This is awkward, but then I cannot overload '.' of VectorWrap!
The problem here is that the conversion to Vector& is not done
automatically, but has to be forced by casting:
vec(rng)=w;
does not work, but
((Vector&) vec(rng))=w;
does. This is awkward to use.
- I could replicate all public methods of Vector in VectorWrap, but this would
be a really poor solution. Also, there is a mutual dependency problem here
(in my case, all these classes are templates!): Vector needs to know
how to create VectorWrap, so I cannot have VectorWrap know about
definitions of Vector! I did not even manage to implement
VectorWrap::operator=(const Vector& arg) {
...
}
this way, which would maybe get " vec(rng) = w;" working (one would
have to use an abstract base of Vector with methods for '=', to get
around the mutual dependency)
Thanks a lot for help!