Topic: multidimensional array using stl vector


Author: pedwards@cs.wright.TOAST.edu (Phil Edwards)
Date: 1997/07/04
Raw View
Peter Mancini <peter_mancini@Sp-am-blo-ck.onesource.com> wrote:
[snip]
+ Is vector a typename?  is vector<int> a typename?  I still don't get why I
+ can't do vector< vector<int> > multidim_array_of_int;

You can.  It's not a very efficient method of storage for an array (of
however many dimensions) whose size isn't going to change at runtime,
however.  There's a lot of wasted memory due to the extra allocation
that's often done by compilers in order to meet the Standard
requirements regarding growth.

I've written a 2D matrix class using vector<double> for one dimension,
and a simple vector<double>* (i.e., the usual array method) as
controlling the other dimension.  It was originally for porting MATLAB
code to C++; since MATLAB matrices may grow arbitrarily during the
course of a program, e.g.,

 an_MbyN_matrix = [ the_same_MbyN_matrix  an_Mby1_column_vector ]

and so I wanted the storage-management routines of vector<>, so that
I'd have to mess with [de]allocation as little as possible.  It's not
very efficient when using matrices which don't change in size.


/dev/phil
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Kresimir Fresl <fresl@grad.hr>
Date: 1997/07/07
Raw View
Peter Mancini wrote:

> [...] OK, so I just answered my own question,

Yes, indeed.

> Is vector a typename?  is vector<int> a typename?

No. Typename is eg. `iterator' in `vector<T>::iterator'.
As far as I can understand it, keyword `typename' is used
to identify qualified names as types (and not as data
members or member functions). I am not sure if it is
necessary in

  typedef typename vector<T>::iterator iterator;

(How can one typedef a data or function member? Can some
kind soul explain that?), but my compiler (which, as
its authors claim, `is the result of serious tracking of
the ANSI/ISO C++ committee deliberations to date', that is,
to `December 9, 1996') didn't complain.

> I still don't get why I
> can't do vector< vector<int> > multidim_array_of_int;

You can. But...

If you declare `ivv' as

  vector< vector<int> > ivv;

you must resize it first

  ivv.resize (no_of_rows);

[with SGI STL: ivv.reserve(...); not the same function, but
there's no `resize()' yet], and then resize all `rows'

  for (int i = 0; i < no_of_rows; ++i)
    ivv[i].resize (no_of_cols);

Of course, you can write

  vector< vector<int> > ivv (no_of_rows);

but not

  vector< vector<int> > ivv (no_of_rows, no_of_cols);

Of course, you can wrap it in a class and let constructor do
the resizing.

But, this approach (with or without wrapping it in a class)
is not very efficent:

1) declaration `vector< vector<int> > ivv (nr);' first calls
   constructor

     vector (nr, vector<int>())

   which calls `new' to allocate memory for
   `vector< vector<int> >', and then calls `vector<int>'s
   default (or copy) constructor `nr' times to initialize
   `nr' empty `vector<int>'s which are members of
   `vector< vector<int> >';

2) then, in a loop, `resize()' is called `nr' times to resize
   individual `rows', and each time `resize()' calls `new'.

Besides, as Phil Edwards points out in his reply, ``There's a
lot of wasted memory due to the extra allocation that's often
done by compilers in order to meet the Standard requirements
regarding growth.''


Approach from `Numerical Recipes' has some other advantages, too.

For example, as all data are (is?) in one memory chunk, in some
(almost all, matrix multiplication is a notable exception) matrix
operations (if you derive `matrix' from `array2d') you can treat
array2d as a long vector:

  template <typename T>
  class array2d {
  // ...
  protected:
    vector<T> elems;         // actual data
    vector<iterator> rows;   // beginnings of rows
  };

  template <typename T>
  class matrix : public array2d<T> {
  public:
    matrix<T>& operator+= (matrix<T> const&);
  // ...
  };

  template <typename T>
  matrix<T>& matrix<T>::operator+= (matrix<T> const& a){
    transform (elems.begin(), elems.end(),
               a.elems.begin(), plus<T>());
    return *this;
  }

You can even combine this with Daveed Vandevoorde's `static
expression trees' (again, matrix multiplication is an
exception).


fres

fresl@grad.hr
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Kresimir Fresl <fresl@grad.hr>
Date: 1997/07/02
Raw View
Peter Kelm wrote:

> I want to create a two-dimensional array using stl vector
> size is determined at runtime. But how do I do this?
>
> #include <vector>
>
> typedef vector<double> row;
> typedef vector<row*>   matrix;
> matrix myMatrix;
>
> the code above works but if I do it like this i cannot use
> the "common" subscript operator
>
> myMatrix[1][2] = 5.4;
>
> Ideas for a simple solution?


My solution (C++ version of 2D arrays in `Numerical Recipes'
2nd ed. by Press et al.) is:

  template <typename T>
  class array2d {
  public:
    typedef typename vector<T>::iterator        iterator;
    typedef typename vector<T>::const_iterator  const_iterator;
    typedef typename vector<T>::size_type       size_type;

    // ...
    array2d (size_type r, size_type c, T const& val = T())
    : elems (r*c, val), rows (r), nr (r), nc (c) {
      init_rows();
    }

    iterator operator[] (size_type i) { return rows[i]; }
    const_iterator operator[] (size_type i) const { return rows[i]; }

  protected:
    vector<T> elems;         // actual data
    vector<iterator> rows;   // beginnings of rows
    size_type nr;            // number of rows
    size_type nc;            // number of columns

    void init_rows() {
      rows[0] = elems.begin();
      for (size_type i = 1; i < nr; ++i)
        rows[i] = rows[i-1] + nc;
    }
  };


In the expression a2d[i][j] first operator[] is
array2d::operator[] which returns vector<T>::iterator
(lets call it `temp') which `points' to the beginning
of the ith row. Because vector<T>::iterator is random
access iterator, there must be (according to the CD)
operator[] defined for it, with operational semantics
*(temp+j).

Dec. '96 CD says that for random acces iterators
temp[j] should only have type convertible to T.
It seems therefore that non-const version of
array2d::operator[] is not guaranteed to work. But
in a recent post (thread `Questions about iterator
library') Brian Parker wrote:

: That's got to be a bug in the draft. The latest SGI version
: of the STL (at www.sgi.com) specifies that for an iterator x,
: *x returns a type convertible to T to allow for a proxy
: implementation, but also specifies that for mutable iterators
: the dereference assignment expression *x = t is defined
: (output iterators are an exception). [...]

: I don't know what is on the committee's agenda, but hopefully
: these bugs will be fixed.

As for current vector implementations, vector<T>::iterator
is simply T* (that is `plain' pointer), and for pointers
operator[] is defined.


fres

fresl@grad.hr


PS. All comments on the above code are welcome.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Peter Mancini" <peter_mancini@Sp-am-blo-ck.onesource.com>
Date: 1997/07/03
Raw View
That's a really interesting solution.  I have a couple of question about
the solution and a general question though.

In the constructor you have elems initialized with:
elems (r*c, val)
How does that work with templates?  I assume that the compiler treats it as
vector<r*c, val) which in VC++ would use:
explicit vector(size_type n, const T& v = T(), const A& al = A());
In VC++ 4.x you would need to indicate the allocator class for A however.

OK, so I just answered my own question, but the second is still a mystery
for me.

Is vector a typename?  is vector<int> a typename?  I still don't get why I
can't do vector< vector<int> > multidim_array_of_int;

--Pete
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]