Re: using vector to encapulate a tree - non const copy constructors
"James Kanze" <james.kanze@gmail.com> wrote in message
news:1164011507.979825.12620@m7g2000cwm.googlegroups.com...
Note too that you won't get very far talking about inheritance.
It is generally acknowledged that you should not inherit from a
standard container.
I am sorry that I have not been able to come back to you over the last week
and thanks very much for the extended and interesting reply.
You make several points and I hope to come back to them in time - but they
have a certain order and it seems natural to first approach your remark that
it is out of fashion, or even dissaproved of to derive from containers.
I agree that there seems to be a predudice against this - but I think it
really is predudice - coming from a historic overinvolvement with
polymorphism. It seems to me that C++ becomes really powerful when one
combines derivation and templates. I find myself wanting to derive from the
map class all the time in my work.
I would argue that one very important (and utterly non-polymorphic!) use of
derivation is to produce distinct classes that have identical functionality.
In particular, this allows type safeness between them.
class A
{
public:
int foo(){}
};
template <int n=0> class B
: private A
{
public:
using A::foo;
};
Then A, B<1> and B<2> have identical public functionality foo, and no
visible base classes but they are distinct classes.
To motivate this example, it might be useful to talk in the language of the
vector class and to loosely imagine that A is std::vector. All instances of
the type std::vector<double> obviously have the same type!! Suppose that
some of these vectors are row vectors and some are column vectors, one could
not build an operator, say operator==() or operator+=() that was typesafe
and which refused to compare row and column vectors.
Now suppose (loosely) that A is the template vector, we could use B<1> to
denote a row vector, and B<2> to be a column vector.
The method described above can be adapted to the real world easily enough
and extra functionality can easily be added. I would be interested in other
efficient approaches to cloning vector<T>. My guess is that those that do
not derive from it are likely to duplicate member functions etc. and be
quite messy. This code should be pretty efficient under most compilers. So
why do the powers that be object.
Best
Terry
Close approximation to the real world solution I use (with variations for
all the STL containers) (sorry about line breaks):
#include <vector>
template <typename T, int id> class my_vector;
// declare the new container class my_vector here so friend functions work
correctly
template <typename T, int id> inline bool operator!= (const typename
my_vector <T,id> & _left, const typename my_vector <T,id> & _right);
//add more comparison operators declarations for >= etc. here so friend
functions work correctly
//
template< class T, int id = 0> class my_vector
: std::vector<T> //private derivation from vector<T>
{
private:
typedef std::vector<T> BASE;
public:
// make member functions of BASE accessible if their references to the
vector object itself are type sensible
using BASE::assign;
using BASE::at;
using BASE::back;
using BASE::begin;
using BASE::capacity;
using BASE::clear;
using BASE::empty;
using BASE::end;
using BASE::erase;
using BASE::front;
using BASE::get_allocator;
using BASE::insert;
using BASE::max_size;
using BASE::pop_back;
using BASE::push_back;
using BASE::rbegin;
using BASE::rend;
using BASE::reserve;
using BASE::resize;
using BASE::size;
//using BASE::swap;
//using BASE::vector;
using BASE::operator[];
using BASE::allocator_type;
using BASE::const_iterator;
using BASE::const_pointer;
using BASE::const_reference;
using BASE::const_reverse_iterator;
using BASE::difference_type;
using BASE::iterator;
using BASE::pointer;
using BASE::reference;
using BASE::reverse_iterator;
using BASE::size_type;
using BASE::value_type;
//keep track of unary constructors as they can take arguments that depend
on the type my_vector
my_vector()
:BASE()
{
}
explicit my_vector(size_type _Count)
:BASE(_Count)
{
}
explicit my_vector(const typename allocator_type& _Al)
:BASE(_Al)
{
}
my_vector(const my_vector& _Right)
:BASE(_Right)
{
}
//use templates for other constructors
template <typename U, typename V>
my_vector(U x, V y)
:BASE(x,y)
{
}
template <typename U, typename V, typename W>
my_vector(U x, V y, W z)
:BASE(x,y,z)
{
}
my_vector & swap ( my_vector & _Right )
{
BASE::swap(static_cast<BASE &>(_Right));
return *this;
}
friend bool operator!= <> (const typename my_vector<T,id> & _left, const
typename my_vector<T,id> & _right);//add more friends
};
template <typename T, int id> inline
bool operator!= (const typename my_vector<T,id> & _left, const typename
my_vector<T,id> & _right)
{
return static_cast< typename my_vector<T,id>::BASE const & >(_left) !=
static_cast<typename my_vector<T,id>::BASE const & >(_right);
}
************************************************************************
// and now a simple (and crudely written) demo to illustrate the template
// my_vector as a container template with vector functionality (except
// it uses the default allocator)
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
typedef my_vector<double,1> column_vector_double;
typedef my_vector<double,2> row_vector_double;
typedef my_vector<int> vector_int;
int main(int argc, char* argv[])
{
vector_int a,b,c;
for (int count =0; count != 10 ; count ++) a.push_back(count);
b.assign(a.begin(),a.end());
c.swap(b);
std::cout << "default constructor, push_back, assign, and swap all used
here\n";
if (b==c) std::cout << "comparison templates called on private base
class\n";
vector_int d(a.begin(),a.end());
vector_int e(d);
std::cout << "range and copy constructor used\n";
double val = e[3];
std::cout << "operator[] used\n";
column_vector_double f(a.begin(),a.end());
column_vector_double g(f);
row_vector_double h(a.begin(),a.end()),r;//r(g) would fail - row and
column
are different types
std::cout << "row and column vector with double values constructed\n";
std::cout << "range constructor used from a controlled sequence of
ints\n";
std::cout << "direct copy construction of a row from a column would fail -
different types\n";
std::ostream_iterator<double> dOutCol ( std::cout , "\n" );
std::ostream_iterator<double> dOutRow ( std::cout , " " );
copy(f.begin(),f.end(),dOutCol);
row_vector_double::iterator it1(g.begin()),it2(g.end());
copy(it1,it2,dOutRow);
std::cout << "\nstd::copy used to copy data to ostream without and with
construction of intermediate iterators\n";
return 0;
}
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]