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
如何让 bb = aa 起作用?
您可以将 A
类型的对象分配给 B
类型的对象并不意味着 list<A>
和 list<B>
也适用。
您可以使用 std::copy
:
std::copy(bb.begin(), bb.end(), std::back_inserter(aa)); // instead of aa = bb
编辑:为了使用它,您必须先调用 aa.resize(bb.size())
,或者最好使用 @Christophe 指出的 back_inserter
。
你应该清楚你在这里做什么:它要么切片(A = B
)要么分配一个非最终的class(B = A
)。为了避免这种情况,您应该使用指针并使用克隆模式。
您可以使用 copy
and a back_inserter
来做到这一点:
std::copy(aa.begin(), aa.end(), back_inserter<list<B>>(bb));
但前提是目标元素(这里是B)可以从源元素(这里是A)构造。所以在这里,您需要一个基于 A 对象的 B 构造函数。
注意:如果你没有目标中复制所需的构造函数class,但你有一些其他方法从源构建目标对象,你可以考虑使用std::transform()
顺便说一下,注意:a=b
有效,但可能会导致 slicing。
就编译器而言,std::list<A>
和 std::list<B>
是不相交的类型。如果您认为 std::list
已在内部为 A
对象分配内存,那么这是有道理的。尝试将 B
个对象分配给 space 可能是灾难性的。
想象一下,例如,B 有一个额外的 属性。现在,如果您试图将 B
存储到足以容纳 A
的内存中,它可能不适合。可以使用类似的逻辑来查看为什么另一个方向也失败了:如果将 A
存储到 space 中以获得 B
,编译器期望额外的 属性 B
它认为 space 是有效的。但是,如果您分配了一个 A
,那么 space 中有谁知道其中的内容。
如果您希望此作业能够工作,您将需要使用某种形式的间接寻址。例如,您可以使用两个 std::list<A*>
实例或两个 std::list<std::shared_ptr<A>>
实例。这样就可以工作了,因为 B*
可以 被安全地视为 A*
,至少假设 类 被正确写入。
你在这里遗漏的是,即使 A
和 B
是相关类型,std::list<A>
和 std::list<B>
实际上是不相关的类型(除了都说list
)。因此,您不能使用复制分配在它们之间进行分配。
但是,假设您可以使用赋值运算符将类型 A
和 B
相互分配,您可以使用 list
的 assign
复制迭代器范围的方法:
aa.assign(bb.begin(), bb.end());
bb.assign(aa.begin(), aa.end());
我认为没有人真正尝试回答如何对 bb = aa;
进行赋值的问题
如前所述,您不能将 std::list<B>
的隐式强制转换或赋值运算符定义为自由运算符。但你有两个选择。他们都依赖于 subclassing 这个集合...只要你不允许任何额外的状态就可以。
选项 #1,是将 BB
定义为 std::list<B>
的子 class,而 bb
将使用这些 class。 (BB
会得到一个额外的赋值运算符。)
选项 #2,是使用助手 class,并且可能更接近您的初衷。助手 class 的定义类似于上述 BB
class,将隐式转换运算符添加回 std::list<B>
.
完整示例如下:
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>
或者您可以直接使用 BB
:
BB bb = aa; // Option #1; use bb just like std::list<B>
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
如何让 bb = aa 起作用?
您可以将 A
类型的对象分配给 B
类型的对象并不意味着 list<A>
和 list<B>
也适用。
您可以使用 std::copy
:
std::copy(bb.begin(), bb.end(), std::back_inserter(aa)); // instead of aa = bb
编辑:为了使用它,您必须先调用 aa.resize(bb.size())
,或者最好使用 @Christophe 指出的 back_inserter
。
你应该清楚你在这里做什么:它要么切片(A = B
)要么分配一个非最终的class(B = A
)。为了避免这种情况,您应该使用指针并使用克隆模式。
您可以使用 copy
and a back_inserter
来做到这一点:
std::copy(aa.begin(), aa.end(), back_inserter<list<B>>(bb));
但前提是目标元素(这里是B)可以从源元素(这里是A)构造。所以在这里,您需要一个基于 A 对象的 B 构造函数。
注意:如果你没有目标中复制所需的构造函数class,但你有一些其他方法从源构建目标对象,你可以考虑使用std::transform()
顺便说一下,注意:a=b
有效,但可能会导致 slicing。
就编译器而言,std::list<A>
和 std::list<B>
是不相交的类型。如果您认为 std::list
已在内部为 A
对象分配内存,那么这是有道理的。尝试将 B
个对象分配给 space 可能是灾难性的。
想象一下,例如,B 有一个额外的 属性。现在,如果您试图将 B
存储到足以容纳 A
的内存中,它可能不适合。可以使用类似的逻辑来查看为什么另一个方向也失败了:如果将 A
存储到 space 中以获得 B
,编译器期望额外的 属性 B
它认为 space 是有效的。但是,如果您分配了一个 A
,那么 space 中有谁知道其中的内容。
如果您希望此作业能够工作,您将需要使用某种形式的间接寻址。例如,您可以使用两个 std::list<A*>
实例或两个 std::list<std::shared_ptr<A>>
实例。这样就可以工作了,因为 B*
可以 被安全地视为 A*
,至少假设 类 被正确写入。
你在这里遗漏的是,即使 A
和 B
是相关类型,std::list<A>
和 std::list<B>
实际上是不相关的类型(除了都说list
)。因此,您不能使用复制分配在它们之间进行分配。
但是,假设您可以使用赋值运算符将类型 A
和 B
相互分配,您可以使用 list
的 assign
复制迭代器范围的方法:
aa.assign(bb.begin(), bb.end());
bb.assign(aa.begin(), aa.end());
我认为没有人真正尝试回答如何对 bb = aa;
如前所述,您不能将 std::list<B>
的隐式强制转换或赋值运算符定义为自由运算符。但你有两个选择。他们都依赖于 subclassing 这个集合...只要你不允许任何额外的状态就可以。
选项 #1,是将 BB
定义为 std::list<B>
的子 class,而 bb
将使用这些 class。 (BB
会得到一个额外的赋值运算符。)
选项 #2,是使用助手 class,并且可能更接近您的初衷。助手 class 的定义类似于上述 BB
class,将隐式转换运算符添加回 std::list<B>
.
完整示例如下:
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>
或者您可以直接使用 BB
:
BB bb = aa; // Option #1; use bb just like std::list<B>