Dav*_*men 10 c++ swap copy-constructor
我试图解决的问题产生于容器,例如std::vector包含引用和const数据成员的对象:
struct Foo;
struct Bar {
Bar (Foo & foo, int num) : foo_reference(foo), number(num) {}
private:
Foo & foo_reference;
const int number;
// Mutable member data elided
};
struct Baz {
std::vector<Bar> bar_vector;
};
Run Code Online (Sandbox Code Playgroud)
这不会按原样工作,因为Foo由于引用成员foo_reference和const成员,无法构建类的默认赋值运算符number.
一种解决方案是将其更改foo_reference为指针并删除const关键字.然而,这会失去引用优于指针的优点,而该const成员确实应该这样做const.他们是私人成员,所以唯一可以造成伤害的是我自己的代码,但是我用自己的代码射击了自己的脚(或更高).
我已经看到了解决这一问题的形式在网络上swap出现的基础上的奇迹被座充满不确定的行为方式reinterpret_cast和const_cast.碰巧这些技术确实在我的计算机上运行.今天.使用一个特定编译器的特定版本.明天还是用不同的编译器?谁知道.我不打算使用依赖于未定义行为的解决方案.
stackoverflow的相关问题:
那么有没有办法swap为这样一个不调用未定义行为的类编写方法/复制构造函数,或者我只是搞砸了?
编辑
只是为了说清楚,我已经非常了解这个解决方案:
struct Bar {
Bar (Foo & foo, int num) : foo_ptr(&foo), number(num) {}
private:
Foo * foo_ptr;
int number;
// Mutable member data elided
};
Run Code Online (Sandbox Code Playgroud)
这明确地消除了这一点const,number并消除了隐含const的内容foo_reference.这不是我追求的解决方案.如果这是唯一的非UB解决方案,那就这样吧.我也很清楚这个解决方案:
void swap (Bar & first, Bar & second) {
char temp[sizeof(Bar)];
std::memcpy (temp, &first, sizeof(Bar));
std::memcpy (&first, &second, sizeof(Bar));
std::memcpy (&second, temp, sizeof(Bar));
}
Run Code Online (Sandbox Code Playgroud)
然后使用copy-and-swap编写赋值运算符.这解决了引用和const问题,但它是UB吗?(至少它不使用reinterpret_cast和const_cast.)一些被省略的可变数据是包含std::vectors的对象,所以我不知道这样的浅拷贝是否可以在这里工作.
您无法重置参考.只需将成员存储为指针,就像在具有可分配类的所有其他库中一样.
如果您想保护自己,请将int和指针移动到基类的私有部分.添加受保护的函数以仅公开int成员以进行读取和对指针成员的引用(例如,以防止自己将成员视为数组).
class BarBase
{
Foo* foo;
int number;
protected:
BarBase(Foo& f, int num): foo(&f), number(num) {}
int get_number() const { return number; }
Foo& get_foo() { return *foo; }
const Foo& get_foo() const { return *foo; }
};
struct Bar : private BarBase {
Bar (Foo & foo, int num) : BarBase(foo, num) {}
// Mutable member data elided
};
Run Code Online (Sandbox Code Playgroud)
(顺便说一句,它不一定是基类.也可以是公共访问者的成员.)
如果您使用移动运算符来实现此操作,则有一种方法:
Bar & Bar :: operator = (Bar && source) {
this -> ~ Bar ();
new (this) Bar (std :: move (source));
return *this;
}
Run Code Online (Sandbox Code Playgroud)
你不应该在复制构造函数中使用这个技巧,因为它们经常会抛出异常,这样就不安全。移动构造函数永远不应该抛出异常,所以这应该没问题。
std::vector其他容器现在尽可能利用移动操作,因此调整大小和排序等都可以。
这种方法可以让您保留 const 和引用成员,但仍然无法复制该对象。为此,您必须使用非常量成员和指针成员。
顺便说一句,您永远不应该像非 POD 类型那样使用 memcpy。
对未定义行为投诉的回应。
问题案例似乎是
struct X {
const int & member;
X & operator = (X &&) { ... as above ... }
...
};
X x;
const int & foo = x.member;
X = std :: move (some_other_X);
// foo is no longer valid
Run Code Online (Sandbox Code Playgroud)
确实,如果您继续使用,这是未定义的行为foo。对我来说这和
X * x = new X ();
const int & foo = x.member;
delete x;
Run Code Online (Sandbox Code Playgroud)
其中很明显 usingfoo是无效的。
也许天真的阅读X::operator=(X&&)会导致你认为foo移动后也许仍然有效,有点像这样
const int & (X::*ptr) = &X::member;
X x;
// x.*ptr is x.member
X = std :: move (some_other_X);
// x.*ptr is STILL x.member
Run Code Online (Sandbox Code Playgroud)
成员指针在ptr的移动后仍然存在,x但foo没有。