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.