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; };
这不会按原样工作,因为由于引用成员foo_reference和const成员编号,Foo类的默认赋值运算符无法构建.
一个解决方案是将foo_reference更改为指针,并摆脱const关键字.然而,这种引用超出了指针的优点,而且这个const成员真的应该是const.他们是私人会员,所以唯一可以伤害的是我自己的代码,但是我用脚本(或更高版本)用自己的代码开枪.
我已经在网络上看到了解决这个问题的解决方案,其中swap方法看起来充满了基于reinterpret_cast和const_cast的奇迹的未定义的行为.发生这些技术似乎在我的电脑上工作.今天.具有一个特定编译器的特定版本.明天还是用不同的编译器?谁知道.我不会使用依赖于未定义行为的解决方案.
stackoverflow的相关答案:
> Does it make sense to implement the copy-assignment operator in a class with all const data members?
第一个答案有趣的线条:“如果那个是不可变的,你的螺丝”.
> Swap method with const members
第一个答案在这里并不真正适用,第二个是有点污泥.
那么有没有办法为这样的类写一个不调用未定义行为的交换方法/复制构造函数,或者我刚刚搞砸了?
编辑
为了说清楚,我已经很清楚这个解决方案:
struct Bar { Bar (Foo & foo,int num) : foo_ptr(&foo),number(num) {} private: Foo * foo_ptr; int number; // Mutable member data elided };
这显然消除了数字的常数,并消除了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)); }
然后使用copy-and-swap写入赋值运算符.这会绕过引用和const问题,但它是UB吗? (至少它不使用reinterpret_cast和const_cast.)一些可爱的可变数据是包含std :: vectors的对象,所以我不知道这样一个浅的副本是否会在这里工作.
解决方法
Bar & Bar :: operator = (Bar && source) { this -> ~ Bar (); new (this) Bar (std :: move (source)); return *this; }
你不应该真正使用这个技巧与复制构造函数,因为它们可以经常抛出,然后这是不安全的.移动构造函数永远不会抛出,所以这应该是可以的.
std :: vector和其他容器现在在可能的情况下利用移动操作,所以调整大小和排序等等都可以.
这种方法将允许您保留const和引用成员,但是您仍然无法复制对象.要做到这一点,你必须使用非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
如果您继续使用foo,那么它是未定义的行为.对我来说这是一样的
X * x = new X (); const int & foo = x.member; delete x;
其中很明显使用foo是无效的.
也许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
成员指针ptr在x的移动中幸存下来,但是foo没有.