tags:

views:

2428

answers:

4

Is there a good way in C++ to implement (or fake) a type for a generic vector of vectors?

Ignore the issue of when a vector of vectors is a good idea (unless there's something equivalent which is always better). Assume that it does accurately model the problem, and that a matrix does not accurately model the problem. Assume also that templated functions taking these things as parameters do need to manipulate the structure (e.g. calling push_back), so they can't just take a generic type supporting [][].

What I want to do is:

template<typename T>
typedef vector< vector<T> > vecvec;

vecvec<int> intSequences;
vecvec<string> stringSequences;

but of course that's not possible, since typedef can't be templated.

#define vecvec(T) vector< vector<T> >

is close, and would save duplicating the type across every templated function which operates on vecvecs, but would not be popular with most C++ programmers.

+24  A: 

You want to have template-typedefs. That is not yet supported in the current C++. A workaround is to do

template<typename T>
struct vecvec {
     typedef std::vector< std::vector<T> > type;
};

int main() {
    vecvec<int>::type intSequences;
    vecvec<std::string>::type stringSequences;
}

In the next C++ (called c++0x, c++1x due to 2010), this would be possible:

template<typename T>
using vecvec = std::vector< std::vector<T> >;
Johannes Schaub - litb
I think they've also fixed the need for spaces in >>
Dave Hillier
indeed, they have :)
Johannes Schaub - litb
+3  A: 

You can simply create a new template :

#include <string>
#include <vector>

template<typename T>
struct vecvec : public std::vector< std::vector<T> > {};

int main() 
{
    vecvec<int> intSequences;
    vecvec<std::string> stringSequences;
}

If you do that you have to remember that destructor of vector is not virtual and not to do something like this :

void test()
{
    std::vector< std::vector<int> >* pvv = new vecvec<int>;
    delete pvv;
}
Rexxar
You'll run into losing all the handy constructors of vector<T>. You need to define them, just passing the arguments to the parent. A possibility, but not a lean-and-mean solution.
xtofl
+3  A: 

I use Boost.MultiArray which is implemented in the boost library.

HTH

lmmilewski
+1 for mentioning this library
mloskot
A: 

You can implement basic vector-of-vector type using std::vector as a basis:

#include <iostream>
#include <ostream>
#include <vector>
using namespace std;

template <typename T>
struct vecvec
{
    typedef vector<T> value_type;
    typedef vector<value_type> type;
    typedef typename type::size_type size_type;
    typedef typename type::reference reference;
    typedef typename type::const_reference const_reference;

    vecvec(size_type first, size_type second)
        : v_(first, value_type(second, T()))
    {}

    reference operator[](size_type n)
    { return v_[n]; }

    const_reference operator[](size_type n) const
    { return v_[n]; }

    size_type first_size() const
    { return v_.size(); }

    size_type second_size() const
    { return v_.empty() ? 0 : v_[0].size(); }

    // TODO: replicate std::vector interface if needed, like
    //iterator begin();
    //iterator end();

private:
    type v_;

};

// for convenient printing only
template <typename T> 
ostream& operator<<(ostream& os, vecvec<T> const& v)
{
    typedef vecvec<T> v_t;
    typedef typename v_t::value_type vv_t;
    for (typename v_t::size_type i = 0; i < v.first_size(); ++i)
    {
        for (typename vv_t::size_type j = 0; j < v.second_size(); ++j)
        {
            os << v[i][j] << '\t';
        }
        os << endl;
    }
    return os;
}

int main()
{
    vecvec<int> v(2, 3);
    cout << v.first_size() << " x " << v.second_size() << endl;
    cout << v << endl;

    v[0][0] = 1; v[0][1] = 3; v[0][2] = 5;
    v[1][0] = 2; v[1][1] = 4; v[1][2] = 6;
    cout << v << endl;
}

It's just a very simple container that mimics a matrix (as long as user promises, by improving vecvec definition or by proper use, rectangular shape).

mloskot
Unfortunately a matrix does not model the problem. The data isn't "rectangular", different rows are of different lengths.
Steve Jessop
@Steve Jessop - generally, while working with vector of vectors, yes there is such eventuality because of exposure of push_back etc., but it is supposed to be an illustration, a primitive solution and I left improvements to reader, i.e. to guarantee common size for rows is maintained, not to expose operations that could modify dimension of row vector, etc.
mloskot