views:

144

answers:

6

I've clearly been stuck in Java land for too long... Is it possible to do the C++ equivalent of the following Java code:

interface Foo {}

class Bar implements Foo {}

static List<Foo> getFoo() {
  return new LinkedList<Foo>();
}

static List<Bar> getBar() {
  return new LinkedList<Bar>();
}

List<? extends Foo> stuff = getBar();

Where Foo is a sub-class of Bar.

So in C++....

std::list<Bar> * getBars()
{
  std::list<Bar> * bars = new std::list<Bar>;
  return bars;
}

std::list<Foo> * stuff = getBars();

Hope that makes sense....

+1  A: 

everything is fine except you return reference to local variable. it is hold in stack and will be released after function returns.

Andrey
Well, I'm lost. I just tried his Java and is C++ code, and neither works. Neither language has covariant generics/templates, so how is this supposed to work?!
Space_C0wb0y
@Space_C0wb0y - i just realised that he has Foo and Boo... this should not work, you are right
Andrey
+2  A: 

you need the object to support constructors for those operations, so you shouldn't return a refernce, rather a straight object that will be copied, else return a pointer ie:

std::list<Bar> getBars()
{
    std::list<Bar> Bars;
    return Bars;
}

or

std::list<Bar>* getBars()
{
    return new std::list<Bar>();
}

std::list supports copying the objects of another list, but only if that list is of the same type, ie: you cannot copy from std::list<Bar> to std::list<Foo> implicitly, but you can copy from std::list<Foo> to std::list<Foo>.

Necrolis
How would you do that?
Snake
Be warned that this will almost certainly result in a memory-leak.
Space_C0wb0y
yes, if your not careful it will, best is to wrap it in a smart/shared pointer, so it gets auto delete when its no longer in use
Necrolis
+4  A: 

Change

std::list<Bar> & getBars()

to

std::list<Bar> getBars()

This will in principle return by value, but there's copy construction eliding mechanism, so optimizing compilers should be able optimize

std::list<Bar> bars (getBars());

to not involve the copy constructor and just directly build bars.

Additionally, in C++0x there is move constructor, so the above code is efficient even if copy/move is not elided away for any reason.

EDIT for the missed subclass question:

In general this is not possible to do cleanly in C++ (I'm assuming that Foo is a superclass of Bar, otherwise what you are doing makes little sense). It is probably possible with some reinterpret_cast black magic, but I'd strongly advise against it.

Closest approximation is probably this (with getBars() adjusted accordingly):

std::list <Bar*>  bars (getBars ());
std::list <Foo*>  foos (bars.begin (), bars.end ());

However, this imposes some non-trivial memory management burden on you. Using some sort of auto-pointers might help, but as far as I remember only shared_ptr can be used in containers.

Finally, Foo/Bar class hierarchy should use virtual functions, otherwise what you want to do is almost certainly not going to work (i.e. unless you really want to ignore any overrides in the subclass).

doublep
-1 because you do not answer the authors question for a covariant assignment of `std::list<Bar>` to `std::list<Foo>`.
Space_C0wb0y
@Space_C0wb0y: Right, I missed the question. The edit covers it, though it doesn't provide a direct answer.
doublep
Removed the downvote. However I still wonder why nobody sees that this doesn't even work in Java.
Space_C0wb0y
@Space_C0wb0y: In that exact form it doesn't work on Java, yes. But e.g. `List <? extends Foo> foos = new ArrayList <Bar> ();` is a valid statement in Java.
doublep
This works if `Bar` is a subclass of `Foo`. The OP has it the other way around.
Space_C0wb0y
@C0wb0y: Yes, but as you wrote yourself, "Also, this would only make sense if `Bar` was a subclass of `Foo`", so I'm assuming the original contains a "mindo".
doublep
A: 

Also, new std::list will return a pointer, not a reference.

DeadMG
+2  A: 

No in my opinon that does not make sense in C++.

First you return a reference that does not exist anymore. To avoid this, you can pass your std::list as a reference parameter to be modified in the function, as

void fillFoos( std::list< Foo > & foos )

Second, foos are not bars and can't be copied one to another, except I think if you provide the right copy operator.

But if you use inheritance, all your foos and bars should be pointers (and if you can smart ones as shared_ptr pointers from boost or tr1). But that doesn't mean the copy works.

I'm not really sure about what you want to do, but transposing from JAVA to C++ in this case does not work. If you create foos, they will have everything from bars automatically.

std::list< Foo > foos; // just work fine

If you want a list of bars constructed as foos:

std::list< Bar * > bars;
bars.push_back( new Foo() );

Or as I would put it in real C++ code with shared_ptr:

typedef boost::shared_ptr< Bar >;
typedef boost::shared_ptr< Foo >;
typedef std::list< BarPtr > BarList;

BarList bars;

bars.push_back( FooPtr( new Foo() ) );
Nikko
+2  A: 

Either I am getting something totally wrong here, or your Java-Code doesn't work. I just tried. It doesn't work. The reason being, that generic containers in Java are not covariant (this means, it also doesn't work if Bar is a subclass of Foo). Secondly, if Foo is a subclass of Bar, then you can assign Foo references to Bar, but not the other way around (neither in Java nor in C++).

Containers in C++ are also not covariant. But you can do

std::list<Foo*> FooList;
// fill FooList;
std::list<Bar*> BarList(FooList.begin(), FooList.end());

if Foo is a subclass of Bar. This will however have the effect that all the pointers in FooList are copied into BarList. That means, if you change FooList afterwards by adding or removing elements, these changes will not be reflected in BarList.

Space_C0wb0y