简体   繁体   English

C ++,std :: list,赋值,继承

[英]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 ). 你在这里缺少的是,即使AB是相关类型, 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: 但是,假设您可以使用赋值运算符将类型AB相互分配,则可以使用listassign方法来复制迭代器的范围:

aa.assign(bb.begin(), bb.end());
bb.assign(aa.begin(), aa.end());

You can do this with a copy and a back_inserter : 您可以使用copyback_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构造函数。

Online demo 在线演示

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM