C ++, std :: list, assign, inheritance

class A, B; class A { public: A& operator= ( const A &rhs ) { return *this; } }; class B: public A { public: B& operator= ( const A &rhs ) { return *this; } }; A a; B b; std::list < A > aa; std::list < B > bb; a = b; // works b = a; // works // aa = bb; // fails // bb = aa; // fails 

How do I get bb = aa?

+7
c ++ inheritance assignment-operator stdlist
source share
5 answers

What you are missing is that even if A and B are related types, std::list<A> and std::list<B> not actually related (except list ). Therefore, you cannot use copy assignment to assign between.

However, assuming that you normally assign types A and B each other using their assignment operators, you can use the list assign method to copy a range of iterators:

 aa.assign(bb.begin(), bb.end()); bb.assign(aa.begin(), aa.end()); 
+6
source share

You can do this with copy and back_inserter :

 std::copy(aa.begin(), aa.end(), back_inserter<list<B>>(bb)); 

but provided that the target element (here B) can be constructed from the original element (here A). So, here you will need constructor B based on object A.

Online demo

Note. If you don't have the constructor needed for a copy in the target class, but you have another way to create the target from the source code, you might consider using std::transform()

By the way, note: a=b works, but can lead to slicing .

+4
source share

Just because you can assign an object of type A object of type B does not mean that this is done for list<A> and list<B> .

Instead of std::copy you can use

 std::copy(bb.begin(), bb.end(), std::back_inserter(aa)); // instead of aa = bb 

EDIT: in order to use this, you need to either call aa.resize(bb.size()) , or it is better to use back_inserter as pointed out by @Christophe.


You must clearly understand what you are doing here: either slicing ( A = B ) or assigning a non-final class ( B = A ). To avoid this, you must work with pointers and use the cloning pattern.

+3
source share

As for the compiler, std::list<A> and std::list<B> are disjoint types. This makes sense when you consider that std::list has internal allocated memory for objects A Trying to assign objects B to this space can be catastrophic.

Imagine, for example, that B has an additional property. Now, if you are trying to keep B in memory large enough for A , this is most likely not suitable. You can use the same logic to understand why the other direction also fails: if you store A in the space for B , the compiler expects that the additional property B , which in his opinion is in this space, is valid, If you assigned A , although in this space there is someone who knows what is in it.

If you want this task to work, you will need to use some form of indirection. For example, you can use two instances of std::list<A*> or two instances of std::list<std::shared_ptr<A>> . This then works because B* can be safely regarded as A* , at least assuming the classes are correctly written.

+2
source share

I do not think that someone was really trying to answer the question of how to fulfill the assignment bb = aa;

As mentioned earlier, you cannot define an implicit cast or assign statement for std::list<B> as a free statement. But you have two options. Both of them rely on subclassing this collection ... this is normal if you do not allow any additional state.

Option # 1 is to define BB as a subclass of std::list<B> and BB will use these classes. ( BB will receive an additional assignment operator.)

Option number 2 is to use a helper class and, most likely, closer to your original intention. The helper class will be defined as the BB class described above, adding the implicit conversion operator back to std::list<B> .

The following is a complete example:

 class A {}; class B: public A { public: B() = default; B( const A &rhs ) {} // Empty because A and B are not defined with any state. B& operator= ( const A &rhs ) { return *this; } }; class BB : public std::list<B> { public: BB() = default; BB(const std::list<A> &aa) { assign( aa.begin(), aa.end() ); } BB& operator= ( const std::list<A> &rhs ) { assign(rhs.begin(), rhs.end()); return *this; } operator std::list<B>() { return *this; } }; // No new member variables or virtual methods are allowed. static_assert( sizeof (std::list<B>) == sizeof (BB), "Bad derivation of std::list<B>" ); A a; B b; b = a; // works std::list<A> aa; std::list<B> bb; BB helper; bb = helper = aa; // Option #2; bb is std::list<B> 

Or you can just use BB directly:

 BB bb = aa; // Option #1; use bb just like std::list<B> 
+1
source share

All Articles