Topic: overloaded operator []


Author: cigna@helios.phy.ohiou.edu (Dave Cigna)
Date: 30 Jan 94 21:02:56 GMT
Raw View
g2devi@cdf.toronto.edu (Robert N. Deviasse) said:
|In article <CK92yw.69y@oucsace.cs.ohiou.edu> cigna@helios.phy.ohiou.edu (Dave
|Cigna) writes:
|>
|>The solution is simple and painless for everyone: allow overloading [] with
|>more than one argument so that statements like a[i,j]=b[l,m,n] are legal.
|>
|>Nobody would be hurt. No existing code would be broken. No ambiguities
|>would be introduced. (have I missed anything?)
|
|The problem is that it changes the expected behaviour of the [] operator
|since a[1,2] usually means a[2] in C/C++ . . .

This meaning would only change for classes that have [,] defined. I would
expect that anyone using such a class would know better than to use the
comma as a sequencing operator inside []. (You don't try to use commas
as sequencing operators in the () in function calls, do you?)

There is the *potential* for old code to be broken, but only if an existing
class - which no doubt already has some interface defined to handle
multiple indices - subsequently has it's interface changed to make use
of [,]. Moral: don't change class interfaces after they're in use.

|Besides, there *are* several languages I can think of that use () to represent
|array indexing. It's merely a syntactic issue. Some languages use a.putAt(1,2),
|some use [a :putAt 1 2], and some use (putAt a 1 2). I fail to see how
|the () operator can be so unreadable. It's a very small compromise. Don't
|you make those sort of compromises (in terms of scope of problem to be solve,
|and how efficient you need to be) in your numerical work to get reliable
|results?

Hmm, I wonder why I don't program in Lisp. Seriously, I *can* make the
compromise; I *do* make the compromise. Heck, I could program in Lisp
if I had to. The point is that I think that adding [,,,,] would improve
the language.

 -- Dave Cigna




Author: cigna@helios.phy.ohiou.edu (Dave Cigna)
Date: 30 Jan 94 21:03:42 GMT
Raw View
rons@u.washington.edu (Ronald Schoenberg) said:
|
|>BTW: I considered cross posting this to sci.math.num-analysis to generate
|>support from the people that are most likely to use something like this, but
|>resisted the temptation in deference to the noise level here in comp.c++.std.
|>
|
|I don't think you will find much support there anyway.  On sci.math.num-
|analysis there are mostly Fortran programmers with a few Pascal and other
|languages, . . .

NOT TRUE!!! I'd estimate that about 20% of the posts there mention C/C++
by name - more than Fortran! The fact is, most people in math, science
and engineering are begining to admit that C/C++ is a serious alternative
to Fortran. Many of them will *never* use it, but a significant fraction
already are and this fraction is growing fast. It would be a serious
mistake to dismiss the needs of the people doing numerical work.

 -- Dave Cigna




Author: peters@phibm60c.Physik.Uni-Augsburg.DE (Peter Schmitteckert (Tel. 5977-246))
Date: Sun, 30 Jan 1994 19:44:05 GMT
Raw View
Hallo,

In  a follup to Dave-Cigna I wrote:
> I agree with Dave Cigna, that '[]' should be used for indexes and
> '()' for function-arguments and that these notations should not be mixed.

That's still my opinion, but reading the other follopups, I agree, that
allowing to overload [] to [i,j] may be in conflict with existing code.

Based on a idea of Damian Conway (who posted a suggestion on my question
on constructors without implicit-type-conversion) I suggest
the following solution to the problem:

Define an Index Class and an operator | (I think overloading ',' is dangerous)
to seperate the Indices.

  class MatrixIndex
  { private:
          unsigned int Index;
          friend unsigned int operator|(Index,Index);

   public:
          MatrixIndex(unsigned int a) : Index(a) {};
  };

  friend unsigned int operator|(MatrixIndex i,MatrixIndex j)
  { // ...  calculate Index ...
  }

  class Matrix { ... }

Using an indexing class and an appropriate matrix-class, one can index
matrices in th following way: A[ i | j ].
It shouldn't be difficult to extend this idea to matrices with more
than 2 indices.

In additian, one may introduce a
class MatrixIndexCalculated;
and replace MatrixClass::operator| :
MatrixIndexCalculated operator|(MatrixIndex i,MatrixIndex j); .

Besides, this might be an idea to implement co- and contravariant matrices
( These are matrices wich have 'upper' and 'lower' indices, in TeX-notation
   e.g. $A^{ij}_{lm}$ )
using a second type of indexing-operator, e.g. || : A[ i | j || l | m ].

Regards Peter Schmitteckert

--------------------------------------------

Peter Schmitteckert
Lehrstuhl fuer Theoret. Physik 2
Institut fuer Physik
Uni Augsburg (Germany)

e-mail: Schmitteckert@Uni-Augsburg.de
Phone:  +49-821-5977-246




Author: kanze@us-es.sel.de (James Kanze)
Date: 01 Feb 1994 19:04:38 GMT
Raw View
In article <CK92yw.69y@oucsace.cs.ohiou.edu>
cigna@helios.phy.ohiou.edu (Dave Cigna) writes:

|> The point is this: although [][] on built in types is interpreted as two
|> operations (never mind how it's actually handled by the compiler) a user
|> defined matrix class (for example) is a single object and referencing the
|> ith, jth element is a *single* operation. I require the matrix to be
|> stored in memory in a specific way, so defining the class as a simple vector
|> of vectors is out of the question. It would, I suppose, be possible to
|> do memory allocation in the matrix constructor and define vectors of
|> pointers, but this leads to complicated scoping, memory bloat, and
|> (perhaps) poor performance, the last two of which are fatal to numerical
|> work.

It would also be possible that operator[] return a helper class, which
itself contains an operator[] which does the actual access.  This
would entail no extra memory.  If all of the functions in the helper
class were inline, then the compiler *should* be able to optimize it
to the same code that would have occured without the helper class.

|> Besides, it would just be a work-around for a missing feature in the
|> language. Note that I've been using a matrix class as an example because
|> it's of personal interest to me, but the same applies to any class that
|> requires more than one index.

The problem is that there are too many "missing features".  Everyone's
list is different.  C++ is big enough as it is; do you really want to
make it bigger?
--
James Kanze                             email: kanze@us-es.sel.de
GABI Software, Sarl., 8 rue du Faisan, F-67000 Strasbourg, France
Conseils en informatique industrielle --
                   -- Beratung in industrieller Datenverarbeitung




Author: haydens@wayback.atc.ll.mit.edu (Hayden Schultz x3685 )
Date: 27 Jan 1994 23:40:48 GMT
Raw View
Dave> The point is this: although [][] on built in types is
Dave> interpreted as two operations (never mind how it's actually
Dave> handled by the compiler) a user defined matrix class (for
Dave> example) is a single object and referencing the ith, jth element
Dave> is a *single* operation. I require the matrix to be stored in
Dave> memory in a specific way, so defining the class as a simple
Dave> vector of vectors is out of the question. It would, I suppose,
Dave> be possible to do memory allocation in the matrix constructor
Dave> and define vectors of pointers, but this leads to complicated
Dave> scoping, memory bloat, and (perhaps) poor performance, the last
Dave> two of which are fatal to numerical work. Besides, it would just
Dave> be a work-around for a missing feature in the language. Note
Dave> that I've been using a matrix class as an example because it's
Dave> of personal interest to me, but the same applies to any class
Dave> that requires more than one index.

I don't think it's quite as bad as you make it out to be.  If your
compiler has reasonable optimization then I don't see why this must be
a burden, either in efficiency or in memory. Here's a simple example.

#include <stream.h>

class IntMatrix {
    int  i;

  public:
    IntMatrix() : i(0) {}

    int &Index(int row, int col) {
 cout << "Index[" << row << "][" << col << "]" << endl;
 return i;
    }

    class IntMatrixHelper {
 IntMatrix *m;
 int  row;

      public:
 IntMatrixHelper(IntMatrix *mat, int r) : m(mat), row(r) {}

 int &operator[](int col) { return m->Index(row, col); }

    };


    IntMatrixHelper operator[](int row) {
 return IntMatrixHelper(this, row);
    }

};


main()
{
    IntMatrix m;

    m[5][6];
}


Here's what cfront does with this. I've run it through cback, c++filt,
and replaced a computer generated random var name with temp1.

int main ()
{ _main ();
  {
    struct IntMatrix   m;
    struct IntMatrix::IntMatrixHelper  temp1;

    m.i = 0;

    { struct IntMatrix::IntMatrixHelper __Q50;

      temp1.m = &m;
      temp1.row = 5;
      __Q50 = temp1;

      IntMatrix::Index(int,int) (__Q50.m, __Q50.row, 6);
    }
  }
}

Ok, so cfront's not optimally smart about this-- but it doesn't have
to be. The C compiler`s optimization should be able to remove the
common expressions and make it as efficient as calling m.Index(5,6).

The IntMatrixHelper class caused the compiler to forget that
IntMatrix::Index was inline. So you do loose something with this
particular c++ language system. But it's not a really big loss.

If I had a choice between asking my compiler writers to make a new
operator[][], or make their compiler smarter about this sort of thing,
I'd ask for more work making the compiler better. Once you got an
operator[][], I'll bet next week you'll be after an operator[][][],
and so on. This way is a little bit more complex, but it is more
general.

 Hayden



--
Hayden Schultz
haydens@ll.mit.edu
MIT Lincoln Lab
244 Wood St.
Lexington, MA, 02173
(617) 981-3685





Author: cigna@helios.phy.ohiou.edu (Dave Cigna)
Date: Wed, 26 Jan 1994 18:49:42 GMT
Raw View
My apologies if my original post was too long winded and poorly put; the
issue that I was trying to raise does pertain to the upcoming C++ standard.

The point is this: although [][] on built in types is interpreted as two
operations (never mind how it's actually handled by the compiler) a user
defined matrix class (for example) is a single object and referencing the
ith, jth element is a *single* operation. I require the matrix to be
stored in memory in a specific way, so defining the class as a simple vector
of vectors is out of the question. It would, I suppose, be possible to
do memory allocation in the matrix constructor and define vectors of
pointers, but this leads to complicated scoping, memory bloat, and
(perhaps) poor performance, the last two of which are fatal to numerical
work. Besides, it would just be a work-around for a missing feature in the
language. Note that I've been using a matrix class as an example because
it's of personal interest to me, but the same applies to any class that
requires more than one index.

The standard response to all of this is "just overload ()." But the simple
fact is that we're talking about an indexing operation and the operator to
use is []. Anything else is a work around.

The solution is simple and painless for everyone: allow overloading [] with
more than one argument so that statements like a[i,j]=b[l,m,n] are legal.

Nobody would be hurt. No existing code would be broken. No ambiguities
would be introduced. (have I missed anything?)

BTW: I considered cross posting this to sci.math.num-analysis to generate
support from the people that are most likely to use something like this, but
resisted the temptation in deference to the noise level here in comp.c++.std.

Dave Cigna
cigna@helios.phy.ohiou.edu




Author: peters@phibm60c.Physik.Uni-Augsburg.DE (Peter Schmitteckert (Tel. 5977-246))
Date: Thu, 27 Jan 1994 16:27:24 GMT
Raw View
In article <CK92yw.69y@oucsace.cs.ohiou.edu>, cigna@helios.phy.ohiou.edu (Dave Cigna) writes:

|> The point is this: although [][] on built in types is interpreted as two
|> operations (never mind how it's actually handled by the compiler) a user
|> defined matrix class (for example) is a single object and referencing the
|> ith, jth element is a *single* operation. I require the matrix to be
|> stored in memory in a specific way, so defining the class as a simple vector
|> of vectors is out of the question. It would, I suppose, be possible to
|> do memory allocation in the matrix constructor and define vectors of
|> pointers, but this leads to complicated scoping, memory bloat, and
|> (perhaps) poor performance, the last two of which are fatal to numerical
|> work. Besides, it would just be a work-around for a missing feature in the
|> language. Note that I've been using a matrix class as an example because
|> it's of personal interest to me, but the same applies to any class that
|> requires more than one index.
|>
|> The standard response to all of this is "just overload ()." But the simple
|> fact is that we're talking about an indexing operation and the operator to
|> use is []. Anything else is a work around.
|>
|> The solution is simple and painless for everyone: allow overloading [] with
|> more than one argument so that statements like a[i,j]=b[l,m,n] are legal.
|>
|> Nobody would be hurt. No existing code would be broken. No ambiguities
|> would be introduced. (have I missed anything?)
|>

In addition,  imagine you want to I implement a (sparse) matrix of functions:

 double f(index i, index j, double x);

doesn't look as nice and clear as f[i,j](x).

Of course one can use  ff[i][j]( x), but  what can I do,
if I want to change the internal representation of my matrix to a complicated,
high efficient representation using a lot of special features of my specific
type of matrices avoiding f(i,j,x), e.g. I had this problem in my
diploma-thesis.

In addittion, as I'm one of these physicist, which like funny mathematics,
I might want to use  f[i,j,k,l](A[3,2](7.,42)) -- that looks  quite natural for
me --. I think that this notation is more readable and
more flexible than the standart C notation.

Overloading () woul lead to  f(i,j,k,l,A(3,2,7.,42)), which is as clear
as the suggested notation.

I agree with Dave Cigna, that '[]' should be used for indexes and
'()' for function-arguments and that these notations should not be mixed

Regards Peter Schmitteckert

============================================

Peter Schmitteckert
Lehrstuhl fuer Theoret. Physik 2
Institut fuer Physik
Uni Augsburg (Germany)

e-mail: Schmitteckert@Uni-Augsburg.de
Phone:  +49-821-5977-246




Author: daniels@biles.com (Brad Daniels)
Date: Thu, 27 Jan 1994 15:20:03 GMT
Raw View
In article <CK92yw.69y@oucsace.cs.ohiou.edu>,
Dave Cigna <cigna@helios.phy.ohiou.edu> wrote:
...
>The point is this: although [][] on built in types is interpreted as two
>operations (never mind how it's actually handled by the compiler) a user
>defined matrix class (for example) is a single object and referencing the
>ith, jth element is a *single* operation. I require the matrix to be
>stored in memory in a specific way, so defining the class as a simple vector
>of vectors is out of the question. It would, I suppose, be possible to
>do memory allocation in the matrix constructor and define vectors of
>pointers, but this leads to complicated scoping, memory bloat, and
>(perhaps) poor performance, the last two of which are fatal to numerical
>work. Besides, it would just be a work-around for a missing feature in the
>language. Note that I've been using a matrix class as an example because
>it's of personal interest to me, but the same applies to any class that
>requires more than one index.
>
>The standard response to all of this is "just overload ()." But the simple
>fact is that we're talking about an indexing operation and the operator to
>use is []. Anything else is a work around.

Well...  operator() really is the simplest answer, but there is a way to
use operator[]...  Simply define two classes, where the second represents
a row of the matrix.  Then have operator[] on the matrix class return a
row object, and operator[] on the row return a scalar element.  If the
layout of the matrix is really weird, you could just make the "row" class
a friend of the matrix class which grabs pieces out of the matrix.  E.g.:

class IntMatrixRow;
class IntMatrix {
    friend class IntMatrixRow;
    // data members...
  public:
    // ...
    int operator()(int i, int j) { /* ... */ } // Simple retrieval of mat[i][j]
    inline IntMatrixRow operator[](int rowNum);
};

class IntMatrixRow {
    IntMatrix *mat;
    int rowNum;
  public:
    IntMatrixRow(IntMatrix *m, int r) : mat(m), rowNum(r) {}
    IntMatrixRow(IntMatrixRow &r) : mat(r.mat), rowNum(r.rowNum) {}

    int operator[](int colNum) { return (*mat)(rowNum,ColNum); }
};

inline IntMatrixRow IntMatrix::operator[](int rowNum)
{
    return IntMatrixRow(this,rowNum);
}

>The solution is simple and painless for everyone: allow overloading [] with
>more than one argument so that statements like a[i,j]=b[l,m,n] are legal.
>
>Nobody would be hurt. No existing code would be broken. No ambiguities
>would be introduced. (have I missed anything?)

You missed something.  It does introduce an ambiguity, and can break existing
code.  The syntax above is currently legal, since the comma operator can
be used in that context.  The example above is equivalent to a[j] = b[n].
I.e., the compiler evaluates i, throws the value away, then uses j as the
index.  It then does the same on the other side, throwing away the results
of evaluating l and m before using n.  In this particular case it seems
pointless to evaluate a variable and throw away the result, but if you
substitute a function call for one of i, l, or m above, you can see the
problem.

- Brad
------------------------------------------------------------------------
+ Brad Daniels                  | "Let others praise ancient times;    +
+ Biles and Associates          |  I am glad I was born in these."     +
+ These are my views, not B&A's |           - Ovid (43 B.C. - 17 A.D.) +
------------------------------------------------------------------------




Author: g2devi@cdf.toronto.edu (Robert N. Deviasse)
Date: 27 Jan 94 15:29:43 GMT
Raw View
In article <CK92yw.69y@oucsace.cs.ohiou.edu> cigna@helios.phy.ohiou.edu (Dave Cigna) writes:
>My apologies if my original post was too long winded and poorly put; the
>issue that I was trying to raise does pertain to the upcoming C++ standard.
>
>The point is this: although [][] on built in types is interpreted as two
>operations (never mind how it's actually handled by the compiler) a user
>defined matrix class (for example) is a single object and referencing the
>ith, jth element is a *single* operation. I require the matrix to be
>stored in memory in a specific way, so defining the class as a simple vector
>of vectors is out of the question. It would, I suppose, be possible to
>do memory allocation in the matrix constructor and define vectors of
>pointers, but this leads to complicated scoping, memory bloat, and
>(perhaps) poor performance, the last two of which are fatal to numerical
>work. Besides, it would just be a work-around for a missing feature in the
>language. Note that I've been using a matrix class as an example because
>it's of personal interest to me, but the same applies to any class that
>requires more than one index.
>
>The standard response to all of this is "just overload ()." But the simple
>fact is that we're talking about an indexing operation and the operator to
>use is []. Anything else is a work around.
>
>The solution is simple and painless for everyone: allow overloading [] with
>more than one argument so that statements like a[i,j]=b[l,m,n] are legal.
>
>Nobody would be hurt. No existing code would be broken. No ambiguities
>would be introduced. (have I missed anything?)

The problem is that it changes the expected behaviour of the [] operator
since a[1,2] usually means a[2] in C/C++ (personally, I wish the comma
operator were replaced with the ";" operator, with parenthesization where
necessary, but it's far too late for that). C++ does not add any new operators
to C, it only allow operators that don't exist (or already exist) for
certain types to be (re)defined.

Besides, there *are* several languages I can think of that use () to represent
array indexing. It's merely a syntactic issue. Some languages use a.putAt(1,2),
some use [a :putAt 1 2], and some use (putAt a 1 2). I fail to see how
the () operator can be so unreadable. It's a very small compromise. Don't
you make those sort of compromises (in terms of scope of problem to be solve,
and how efficient you need to be) in your numerical work to get reliable
results?

>
>BTW: I considered cross posting this to sci.math.num-analysis to generate
>support from the people that are most likely to use something like this, but
>resisted the temptation in deference to the noise level here in comp.c++.std.
>
>Dave Cigna
>cigna@helios.phy.ohiou.edu


Take care,
    Robert

--
/----------------------------------+------------------------------------------\
| Robert N. Deviasse               |"If we have to re-invent the wheel,       |
| EMAIL: g2devi@cdf.utoronto.ca    |  can we at least make it round this time"|
+----------------------------------+------------------------------------------/




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Fri, 28 Jan 94 15:39:21 PST
Raw View
In article <CKAr1p.Gq6@Yuri.CC.Uni-Augsburg.DE>, peters@phibm60c.Physik.Uni-Augsburg.DE (Peter Schmitteckert (Tel. 5977-246)) writes:
|>
|> Of course one can use  ff[i][j]( x), but  what can I do,
|> if I want to change the internal representation of my matrix to a complicated,
|> high efficient representation using a lot of special features of my specific
|> type of matrices avoiding f(i,j,x), e.g. I had this problem in my
|> diploma-thesis.

The answer, as mentioned before, is to have matrix::operator[](int)
return a friend class that *logically* represents a pointer to arow
of a matrix. This may be implemented as a 'smart pointer' class that
accesses the associated matrix directly.  (That is the data members of
the RowPointer class might be a pointer to a matrix and a row index
to be used in accessing the internals). You would then define an
RowPointer::operator[](int) that returns a single element of the
matrix using both the stored index and the index passed in.  Since
the RowPointer need not actually contain a row, or even directly
reference a row structure in the matrix class, this is not very
expensive.  Furthermore, the RowPointer can use a private 2-parameter
function that does exactly the indexical look-up that your double
indexing operator would have, so you can even use the same algorithm
if you so desire.

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: cigna@helios.phy.ohiou.edu (Mr. Salty)
Date: Sat, 22 Jan 1994 02:27:16 GMT
Raw View
Hello -

It is my ambition to create a matrix class. Ideally, I would like to be
able to overload the (multiple) subscript operator [][] to allow
statements like:

matrix m(4,4);   //a 4x4 matrix
m[1][2] = 5;

Of course I can't overload [][] (at least I haven't figured out how to do
it.) I can overload the function call operator () like this:

matrix m(4,4);
m(1,2) = 5;

but this seems too unnatural. An alternative that I could live with is:

matrix m(4,4);
m[1,2] = 5;

As far as I can tell from the ARM, the overloaded subscript operator [],
and the overloaded function call operator () follow almost same rules.
The one important exception is that () allows an expresion list with zero
or more expressions while [] requires exactly one.

Perhaps someone in the know could explain why I shouldn't be able to
write a member function operator[](int rows, int cols).

Dave Cigna
cigna@helios.phy.ohiou.edu





Author: robert@stud.unit.no (Robert Schmidt)
Date: 22 Jan 1994 20:18:49 GMT
Raw View
Please make a note of the follow-up.  This isn't a standards question.

In article <CK0EtH.7pG@oucsace.cs.ohiou.edu>, cigna@helios.phy.ohiou.edu (Mr. Salty) writes:
|> It is my ambition to create a matrix class. Ideally, I would like to be
|> able to overload the (multiple) subscript operator [][] to allow
|> statements like:
|>
|> matrix m(4,4);   //a 4x4 matrix
|> m[1][2] = 5;
|>
|> Of course I can't overload [][] (at least I haven't figured out how to do
|> it.) I can overload the function call operator () like this:

It might help a little if you reflect on the fact that [][] is not one
operator, but *two*.  Then think of a matrix as a vector of vectors,
and you should have something going.

|> matrix m(4,4);
|> m[1,2] = 5;

This is the same as

m[2] = 5;

due to the list expression "1,2" taking on the value of the last operand.
There is no way you can make the above expression do what you want.
A tempting approach might be to overload int::operator,(int), to gather
ints into a vector of coordinates, which then is passed on to
Vector::operator[Vector &].  But since no int::operators can be overloaded
this won't work.

I hope this cleared things up a little.

As I implied above, a more general approach, using templates, might
prove beneficial.  If you don't feel ready to do this, or if you don't
NEED a general vector/matrix template library, I suggest that you
define a readable member function like this:

float &Matrix::element(int i, int j) { return ...; }
// ...
Matrix m(4,4);
m.element(4,4) = 5;

--
Robert Schmidt - robert@stud.unit.no - Buuud@IRC
Ztiff Zox Softwear: fast/tiny utilities, games/graphics programming on
                    the DOS platform (C/C++/asm).  Suggestions welcome!

I'm sure I mean everything I say, I think.





Author: grumpy@cbnewse.cb.att.com (Paul J Lucas)
Date: Sun, 23 Jan 1994 20:21:28 GMT
Raw View