[ Please don't top post ] - rearranged below...
Salt_Peter wrote:
(e-mail address removed) wrote:
I had a similar problem.
There I had my List<template>, and in case of template class were 'Car'
and could not pass a List<Ferrari> to my function, just like you're
trying to. I had to make a new function, called "Recast".
In this case, I used an rtti function to dynamically cast it, (checking
if it's safe to cast from Ferrari to Cars, throwing an exception if
they can't be casted). You can cut it off, but bad things can happen if
you cast from, like saying, Cars to Ferraris.
/*!
* This is used to recast the whole list because elements can inherit
each other. Win32 only.
*/
template<class NewElementType>
List<NewElementType>&
Recast() {
// we should be aware if both ElementType and NewElementType are
compatible
ElementType tempT;
NewElementType& tempNT = dynamic_cast<NewElementType&>( tempT); //
force runtime to throw exception if we're casting to wrong datatype
List<NewElementType>* recast = (List<NewElementType>*) this;
return *recast;
}
The above invokes undefined behaviour. I can't express to you in words
how dangerous the above code is.
You might think it works in Win32 but in fact it doesn't. Of course, MS
won't tell you that.
Basicly: a recast is dangerous even in the best of conditions - better
a "conversion ctor" which creates a new fully-formed derived object.
And in the event that you are converting a Ferrarri to a car thats done
automatically if the former is derived from the latter( plain copy ctor
invoked). The resulting Cars are spliced Ferraris, of course. Lets
ignore the use of ptrs and polymorphism for now.
The solution is so simple and doesn't need a cast of any kind: a
conversion ctor.
#include <iostream>
#include <list>
#include <algorithm>
struct Car
{
Car() { }
Car(const Car& copy) { }
virtual ~Car() { }
};
struct Ferrarri : public Car
{
Ferrarri() { }
// conversion ctor: (thats all you need)
Ferrarri(const Car& cvt) : Car(cvt) { }
Ferrarri(const Ferrarri& copy) { }
};
int main()
{
std::list< Car > cars(10);
std::list< Ferrarri > racingcars(10);
// the following splices Ferraris into Cars
std::copy(racingcars.begin(), racingcars.end(), cars.begin());
// the following constructs (not cast) brand new Ferrarris
std::copy(cars.begin(), cars.end(), racingcars.begin());
}
Mr. Salt,
The solution is quite simple to Cars/Ferrarri scenario, but the problem
initially pointed still remains.
How to solve it? The following code is invalid:
void something( std::list<Car>& list_of_cars) {
}
std::list<Ferrarri> racingcars(10);
something( racingcars);
That's the error. The dynamic cast will force the runtime to check
inheritance between Ferrari and Cars.
Regards,
Gustavo
That static type check happens without any casting involved. A
container of Ferrarris *is* already a container of Cars.
Now to have something(std::list<>&) work with any Car (Lamborghini,
Carrerra, Aston Martin) - template it:
#include <iostream>
#include <list>
class Car
{
std::string s;
public:
Car(std::string s_ = "Car") : s(s_) { }
const std::string& getstr() const { return s; }
};
class Ferrarri : public Car
{
public:
Ferrarri(const Car& cvt) : Car("Ferrarri") { }
Ferrarri(std::string s_ = "Ferrarri") : Car(s_) { }
};
// something(...) template
template< typename T >
void something( std::list< T >& list_of_cars )
{
typedef typename std::list< T >::iterator TIter;
for( TIter iter = list_of_cars.begin();
iter != list_of_cars.end();
++iter )
{
std::cout << (*iter).getstr();
std::cout << std::endl;
}
}
int main()
{
std::list<Ferrarri> racingcars(5);
something( racingcars );
}
/*
Ferrarri
Ferrarri
Ferrarri
Ferrarri
Ferrarri
*/
Consider the implications. That template will work with *any*
derivative of Car - including a derivative not yet written.