[英]C++, std::list, assignment, 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 to work? 如何让bb = aa工作?
What you're missing here is that even though A
and B
are related types, std::list<A>
and std::list<B>
are actually unrelated types (other than both saying list
). 你在这里缺少的是,即使
A
和B
是相关类型, std::list<A>
和std::list<B>
实际上是不相关的类型(除了两个都说list
)。 As such you can't use copy assignment to assign between them. 因此,您无法使用复制分配在它们之间进行分配。
However, assuming that you're ok assigning the types A
and B
to each other using their assignment operators, you can use list
's assign
method to copy the range of iterators: 但是,假设您可以使用赋值运算符将类型
A
和B
相互分配,则可以使用list
的assign
方法来复制迭代器的范围:
aa.assign(bb.begin(), bb.end());
bb.assign(aa.begin(), aa.end());
You can do this with a copy
and a back_inserter
: 您可以使用
copy
和back_inserter
执行此操作:
std::copy(aa.begin(), aa.end(), back_inserter<list<B>>(bb));
but under the condition that the target element (here B) can be constructed from the source element (here A). 但是在目标元素(这里是B)可以从源元素(这里是A)构造的条件下。 So here, you'd need a B constructor based on an A object.
所以在这里,你需要一个基于A对象的B构造函数。
Note: If you don't have the constructor needed for the copy in the target class, but you have some other way to build a target object from the source, you can consider using std::transform()
注意:如果您没有目标类中的副本所需的构造函数,但是您还有其他方法可以从源构建目标对象,则可以考虑使用
std::transform()
By the way, attention: a=b
works, but might result in slicing . 顺便提一下,注意:
a=b
有效,但可能导致切片 。
That you can assign a object of type A
to an object of type B
does not mean that holds for list<A>
and list<B>
as well. 您可以将类型
A
的对象分配给类型B
的对象,但这并不意味着保留list<A>
和list<B>
。
Instead of you can use std::copy
: 而不是你可以使用
std::copy
:
std::copy(bb.begin(), bb.end(), std::back_inserter(aa)); // instead of aa = bb
EDIT: in order to use that you either have to call aa.resize(bb.size())
first, or better use a back_inserter
as noted by @Christophe. 编辑:为了使用它你必须首先调用
aa.resize(bb.size())
,或者更好地使用@Christophe所指出的back_inserter
。
You should be clear about what you're doing here: it's either slicing ( A = B
) or assigning a non-final class ( B = A
). 你应该清楚你在这里做了什么:它要么切片(
A = B
),要么指定一个非最终类( B = A
)。 In order to avoid that, you should work with pointers and use the clone pattern. 为了避免这种情况,您应该使用指针并使用克隆模式。
As far as the compiler is concerned std::list<A>
and std::list<B>
are disjoint types. 就编译器而言,
std::list<A>
和std::list<B>
是不相交的类型。 This makes sense if you consider that std::list
has internally allocated memory for A
objects. 如果您认为
std::list
已为A
对象内部分配内存,则这是有意义的。 Trying to assign B
objects into that space could be disasterous. 试图将
B
对象分配到该空间可能是灾难性的。
Imagine, for example, that B has an extra property. 想象一下,例如,B有一个额外的属性。 Now, if you're trying to store a
B
into memory large enough for an A
, it will likely not fit. 现在,如果你试图将
B
存储到足够大的A
内存中,它可能不适合。 Similar logic can be used to see why the other direction also fails: if you store an A
into space for a B
, the compiler expects that the extra property of the B
it believes to be at that space is valid. 可以使用类似的逻辑来查看另一个方向也失败的原因:如果将
A
存储到B
空间中,编译器期望它认为在该空间的B
的额外属性是有效的。 If you assigned an A
though, that space has who-knows-what in it. 如果你分配了一个
A
,那个空间有谁知道什么。
If you want this assignment to be able to work, you're going to need to use some form of indirection. 如果您希望此分配能够工作,您将需要使用某种形式的间接。 For example, you could use two
std::list<A*>
instances or two std::list<std::shared_ptr<A>>
instances. 例如,您可以使用两个
std::list<A*>
std::list<std::shared_ptr<A>>
实例或两个std::list<std::shared_ptr<A>>
实例。 This then works because a B*
can be safely treated as an A*
, at least assuming that the classes are properly written. 这之所以有效,是因为
B*
可以安全地被视为A*
,至少假设这些类是正确编写的。
I don't think anyone actually tried to answer the question of how to do the assignment of bb = aa;
我认为没有人真正试图回答如何分配
bb = aa;
As previously mentioned, you cannot define an implicit cast or assignment operator for std::list<B>
as a free operator. 如前所述,您不能将
std::list<B>
的隐式强制转换或赋值运算符定义为自由运算符。 But you do have two options. 但你有两个选择。 Both of them rely on subclassing this collection... which is OK as long as you don't allow any additional state.
它们都依赖于子集化这个集合......只要你不允许任何其他状态就可以了。
Option #1, is to define BB
as subclass of std::list<B>
and bb
would use these classes. 选项#1,是将
BB
定义为std::list<B>
子类, bb
将使用这些类。 ( BB
would get an additional assignment operator.) (
BB
会得到一个额外的赋值运算符。)
Option #2, is to use a helper class, and is likely closer to your original intent. 选项#2,是使用帮助程序类,可能更接近您的原始意图。 The helper class would be defined like the
BB
class described above, adding an implicit conversion operator back to std::list<B>
. 辅助类的定义类似于上面描述的
BB
类,将隐式转换运算符添加回std::list<B>
。
A complete example follows: 一个完整的例子如下:
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 could just use BB
directly: 或者您可以直接使用
BB
:
BB bb = aa; // Option #1; use bb just like std::list<B>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.