construction question

J

John Goche

Hello,

Consider the following example. There is a function

foo(Foo1<Foo2> foo1) { ... }

The class Foo1 has an empty constructor.

Foo2 can be constructed from an instance foo3 of Foo3.

Can I call:

foo(foo3);

?

Thanks,

JG
 
G

Gavin Deane

John said:
Hello,

Consider the following example. There is a function

foo(Foo1<Foo2> foo1) { ... }

The class Foo1 has an empty constructor.

Foo2 can be constructed from an instance foo3 of Foo3.

Can I call:

foo(foo3);

?

The parameter of the function foo is of type Foo1<Foo2>. The object you
pass when you call foo must therefore be of type Foo1<Foo2> or of some
type that can be converted to Foo1<Foo2>. So the only question that
matters is, can an object of type Foo3 be converted to type Foo1<Foo2>.
Unless the answer is yes, your code will not compile.

The fact that a Foo2 can be constructed from a Foo3 is irrelevant. The
function foo does not take a Foo2, it takes a Foo1<Foo2>, which is a
completely different type.

In types that might be more familiar: if Foo1 is std::vector, Foo2 is
std::string and Foo3 is const char*, you have

void foo(std::vector<std::string> v) { ... }

and you are trying to do

foo("some string literal");

A std::vector<std::string> can not be constructed from a string literal
so the code will not compile. A std::string can be constructed from a
string literal, but that is irrelevant because we are not trying to
construct a std::string, we are trying to construct a
std::vector<std::string>.

Gavin Deane
 
S

Salt_Peter

John said:
Hello,

Consider the following example. There is a function

foo(Foo1<Foo2> foo1) { ... }

void foo(Foo1 said:
The class Foo1 has an empty constructor.

You mean a default ctor.
That doesn't matter, Foo1 takes a template parameter and nobody knows
if any default(s) is/are providied in its template list.
Foo2 can be constructed from an instance foo3 of Foo3.

Can I call:

foo(foo3);

only if the following is valid:

Foo1<Foo2> foo1foo2;
Foo3 instance( foo1foo2 );

Which according to the above statement about Foo3, is rather unlikely.

Try:

Foo1< Foo3 > foo1foo3;
foo( foo1foo3 );
 
G

gustavo.scotti

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;
}
 
S

Salt_Peter

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());
}
 
G

gustavo.scotti

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
 
S

Salt_Peter

[ Please don't top post ] - rearranged below...

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.
 
G

Gavin Deane

Salt_Peter said:
That static type check happens without any casting involved. A
container of Ferrarris *is* already a container of Cars.

I think you understand this point because your code did not appear to
take an approach that suggested any misunderstanding, but just in case
of any confusion...

A container of Ferraris *is not* a container of Cars in the
polymorphism sense of "is a". You can not put an Lamborghini in a
container of Ferraris. You can put a Lamborghini in a container of Cars
and that container of Cars might happen to only contain Ferraris before
you put the Lamborghini in. But a container of Cars that happens to
only contain Ferraris is not a container of Ferraris, it is a container
of Cars.

http://www.parashift.com/c++-faq-lite/proper-inheritance.html#faq-21.3

In fact I don't think this is directly relevant to the questioner, as
the code under discussion uses containers of objects, not containers of
pointers or references, so is presumably not trying to store Cars to be
used polymorphically. However, the phrase "is a" is often strongly
associated with inheritance and polymorphism and I wanted to avoid any
misinterpretation of your statement that a container of Ferraris is a
container of Cars.

Gavin Deane
 
G

gustavo.scotti

I'll stop arguing to you because you've been so stubborn to your point
of view.
Your code will allow you to deal with a list of Bananas and not only
Cars.

And semantically speaking, a containter of cars must accept a container
of Ferraris, Porsches, Mercedes, and so on... (but not Apples or
Bananas).



Salt_Peter said:
[ Please don't top post ] - rearranged below...

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.
 
S

Salt_Peter

I'll stop arguing to you because you've been so stubborn to your point
of view.
Your code will allow you to deal with a list of Bananas and not only
Cars.

And semantically speaking, a containter of cars must accept a container
of Ferraris, Porsches, Mercedes, and so on... (but not Apples or
Bananas).

Please don't top post.
Nobody is arguing. And my approach is no better than any other.
However, take note that a container of Cars can store only a mixture of
Car derivatives. It can't hold bananas or trees. Thats not its job, its
not a container of Java objects.
Again, the goal here is to support any car, including a container of
different cars.
Salt_Peter said:
[ 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.
 
O

Old Wolf

Salt_Peter said:
However, take note that a container of Cars can store only a mixture of
Car derivatives.

Not true. It can only store Cars. C++ standard library containers
are all homogeneous, that is, all their elements of the same type.
A list<Car> cannot store a Ferrari.

Of course you could shoe-horn various different objects into
a container by storing their representation, eg. you could have
a list<std::string> and copy all the bytes of the Ferrari into
a string and put it in the list, and then copy them out again
and into a Ferrari structure when you want to get them out of
the list. This appears to be what the OP code is doing, and
is rather poor design to say the least.
 
S

Salt_Peter

Old said:
Not true. It can only store Cars. C++ standard library containers
are all homogeneous, that is, all their elements of the same type.
A list<Car> cannot store a Ferrari.

Says who? If you can store a car, you can store a derivative of it.
To store a Car, i'ld obviously need std::list<Car*> and new whatever(),
of course.
Thats probably what you meant.

But note, i'm not storing Cars, i'm passing a *reference* to a
container of Car derivatives.
Add a virtuall foo() to Car and Ferrarri and the something<> template
calls Ferrarri::foo() without a hitch. References are polymorphic.
Thats not a hack.
Of course you could shoe-horn various different objects into
a container by storing their representation, eg. you could have
a list<std::string> and copy all the bytes of the Ferrari into
a string and put it in the list, and then copy them out again
and into a Ferrari structure when you want to get them out of
the list. This appears to be what the OP code is doing, and
is rather poor design to say the least.

Yes, that i aggree. What specially gets me worked up is seeing
dynamic_casts to convert a base to a child. What the OP could do, as
pointed out, is use a conversion ctor to rebuild the derivative from
base objects - although that is a dubious activity as well, at least
you end up with a valid object.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top