dougm@zamenhof.rice.edu (Doug Moore) (02/23/90)
I can't seem to find a way to do precisely what I want; I want a sparse vector data type. I can declare a float-returning operator [] that returns 0.0 if the array index corresponds to a row for which no nonzero is stored. I can declare a reference-to-float-returning operator[] that creates a new entry in my linked list of nonzero values when the row has no corresponding nonzero stored. Unfortunately, I want one behavior when the pseudo-array access occurs as an rvalue and another when it appears as an lvalue. Can I get what I want? Dougm
daniel@saturn.ucsc.edu (Daniel Edelson) (02/23/90)
In article <5216@brazos.Rice.edu> dougm@zamenhof.rice.edu (Doug Moore) writes: > >I can't seem to find a way to do precisely what I want; I want a >sparse vector data type. I can declare a float-returning operator [] >that returns 0.0 if the array index corresponds to a row for which no >nonzero is stored. I can declare a reference-to-float-returning >operator[] that creates a new entry in my linked list of nonzero >values when the row has no corresponding nonzero stored. >Unfortunately, I want one behavior when the pseudo-array access occurs >as an rvalue and another when it appears as an lvalue. Can I get what >I want? > >Dougm You can have the [] operator return a dummy type and then overload the assignment operator on the dummy type. This is not real efficient but it seems to give correct semantics. I wrote this code for reference counting vectors efficiently, but its not efficient. It should work for sparse vectors well, though. class vector; struct dummy { // A dummy refers to a specific vector element vector * vp; int index; operator int&() { /* return the element value */} void operator=(int & t); dummy(vector *vvpp, int i) : vp(vvpp), index(i) { } }; struct vec { // implementation details are yours dummy & operator[](int index) { return dummy(this, index); } }; inline void dummy::operator=(int & t) { // Do whatever necessary to assign t to the vector // component at index in *vp. } daniel@cis.ucsc.edu daniel edelson
jeffa@hpmwtd.HP.COM (Jeff Aguilera) (02/24/90)
> I can't seem to find a way to do precisely what I want; I want a > sparse vector data type. I can declare a float-returning operator [] > that returns 0.0 if the array index corresponds to a row for which no > nonzero is stored. I can declare a reference-to-float-returning > operator[] that creates a new entry in my linked list of nonzero > values when the row has no corresponding nonzero stored. > Unfortunately, I want one behavior when the pseudo-array access occurs > as an rvalue and another when it appears as an lvalue. Can I get what > I want? Here's one way, but it's not so pretty: float spare_matrix::operator[](int); //read-only access float& spare_matrix::operator()(int); //possible write-to access ----- jeffa
chip@tct.uucp (Chip Salzenberg) (02/24/90)
According to dougm@zamenhof.rice.edu (Doug Moore): >Unfortunately, I want one behavior when the pseudo-array access occurs >as an rvalue and another when it appears as an lvalue. Can I get what >I want? You almost had it. Define a reference_to_missing_array_element type, and have operator[] return an object of that type. Then define: reference_to_missing_array_element::operator float() return 0.0 reference_to_missing_array_element::operator = (float) create the missing array element and assign to it No sweat.
dl@g.g.oswego.edu (Doug Lea) (02/25/90)
> From: chip@tct.uucp (Chip Salzenberg): > > According to dougm@zamenhof.rice.edu (Doug Moore): > >Unfortunately, I want one behavior when the pseudo-array access occurs > >as an rvalue and another when it appears as an lvalue. Can I get what > >I want? > > You almost had it. Define a reference_to_missing_array_element type, > and have operator[] return an object of that type. Then define: > > reference_to_missing_array_element::operator float() > > return 0.0 > > reference_to_missing_array_element::operator = (float) > > create the missing array element and assign to it > > No sweat. > Consider what happens in void inc(float& f) { f += 1.0; } main() { // ... inc(v[0]); } Here, v[0] returns a reference_to_missing_array_element, which is `coerced' to return a constant value (0.0). The 0.0 is made into a temporary variable in order to make a reference. This temporary is then modified in inc(), without touching the underlying representation in v. This is probably not what a user would have in mind. -Doug -- Doug Lea, Computer Science Dept., SUNY Oswego, Oswego, NY, 13126 (315)341-2367 email: dl@oswego.edu or dl%oswego.edu@nisc.nyser.net UUCP :...cornell!devvax!oswego!dl or ...rutgers!sunybcs!oswego!dl
roger@procase.UUCP (Roger H. Scott) (02/27/90)
In article <5216@brazos.Rice.edu> dougm@zamenhof.rice.edu (Doug Moore) writes: > >I can't seem to find a way to do precisely what I want; I want a >sparse vector data type. I can declare a float-returning operator [] >that returns 0.0 if the array index corresponds to a row for which no >nonzero is stored. I can declare a reference-to-float-returning >operator[] that creates a new entry in my linked list of nonzero >values when the row has no corresponding nonzero stored. >Unfortunately, I want one behavior when the pseudo-array access occurs >as an rvalue and another when it appears as an lvalue. Can I get what >I want? I'm so glad you asked about this! This is one of my favorite C++ inventions. typedef unsigned Index; class SomeType; class SparseVector { public: SparseVector(); SparseVector(unsigned size); ~SparseVector(); ... SparseVectorRef operator[](Index); private: ... // guts friend class SparseVectorRef; void storeAt(Index, SomeType); // implementation of store SomeType at(Index); // implementation of retrieval }; class SparseVectorRef { public: operator SomeType() {return v->at(i);} void operator=(SomeType val) {v->storeAt(i, val);} // other functions, such as operator&(), can be added if you want to // emulate C arrays (for whatever twisted reason) private: friend class SparseVector; SparseVectorRef(SparseVector *vv, Index ii) : v(vv), i(ii) {} SparseVector *v; Index i; }; inline SparseVectorRef SparseVector::operator[](Index i) { return SparseVectorRef(this, i); } void example() { SparseVector v10(10); SomeType a, b; v10[3] = a; // v10.operator[](3).operator=(a); b = v10[7]; // b = v10.operator[](7).operator SomeType(); } Now, wasn't that obvious? ;-]
jimad@microsoft.UUCP (Jim ADCOCK) (03/01/90)
/****
You can't make two versions of op[] for lvalue and rvalue, but you can
differentiate the situations where an op[] is taken, and where an op[] is
taken and value is changed. The general trick is for op[] to return a
smart proxy for that element, and a special assignment to the proxy is
defined that updates the vector if the value of the proxy, [and thus the
element] changes. Just to confuse things, in the below sketchy example,
I use the same class "SVEL" for both the elements of the vector, and the
proxy to that element.
****/
class SVEL// a sparse vector element
{
SVEL* psvel;
class SV& sv;
int i;
double d;
public:
SVEL(SV& svT, int iT, SVEL* psvelT, double dT=0.0);
SVEL(SV& svT, int iT);
SVEL& operator=(double d);
void Attach(SVEL* psvelT) { psvel = psvelT; }
SVEL* Next() { return psvel; }
int Index() { return i; }
void Print();
operator double() { return d; }
};
SVEL::SVEL(SV& svT, int iT, SVEL* psvelT, double dT) :
sv(svT), i(iT), psvel(psvelT), d(dT) {}
SVEL::SVEL(SV& svT, int iT) :
sv(svT), i(iT), psvel(0), d(0) {}
void SVEL::Print() { printf("(%d, ",i); printf("%g) ",d); }
class SV // a sparse vector
{
SVEL* psvel;
public:
SV();
void Attach(SVEL* psvelT)
{ SVEL* pT = psvel; psvel = psvelT; psvel->Attach(pT); }
SVEL& operator[](int i);
void Print();
};
SV::SV() : psvel(0) {}
SVEL& SVEL::operator=(double dT)
{
if ((d==0.0) && (dT!=0.0))
{
d = dT;
sv.Attach(this);
}
else d = dT;
return *this;
}
void SV::Print()
{
SVEL* p = psvel;
while (p)
{
p->Print();
p = p->Next();
}
putchar('\n');
}
SVEL& SV::operator[](int i)
{
SVEL* p = psvel;
while (p && (i != p->Index()))
p = p->Next();
if (p)
return *p;
else
return *new SVEL(*this, i);
}
int main()
{
SV v1, v2;
v1[100] = 10.0;
v1[200] = 20.0;
v2[10] = v1[1];
v2[20] = v1[200];
v2[30] = 300.0;
v1.Print();
v2.Print();
v2[20] = 200.0;
v2.Print();
double d20 = v2[20];
printf("%g\n", d20);
return 0;
}
// gc left as an exercise :-)jimad@microsoft.UUCP (Jim ADCOCK) (03/06/90)
In article <DL.90Feb25100053@g.g.oswego.edu> dl@oswego.edu writes:
XConsider what happens in
X
Xvoid inc(float& f) { f += 1.0; }
X
Xmain()
X{
X // ...
X inc(v[0]);
X}
X
XHere, v[0] returns a reference_to_missing_array_element, which is
X`coerced' to return a constant value (0.0). The 0.0 is made into a
Xtemporary variable in order to make a reference. This temporary is
Xthen modified in inc(), without touching the underlying representation
Xin v. This is probably not what a user would have in mind.
The very latest specs on the _language_ call for a very different behavior
in this regard. Namely only if the reference is to a const is a temporary
object created. This prevents the situation mentioned where a hidden
temporary is created, modified, and then ignored -- all done behind the
users back. Instead, this scenerio will give an error. Naturally, it
will take an indeterminate amount of time for these language changes to
filter down to a particular compiler release.
Still, if one is designing a fundamental type like a sparse vector, it
would be nice if it works like a standard "C" vector.