c – 如何复制(或交换)包含引用或const的成员的类型的对象?

前端之家收集整理的这篇文章主要介绍了c – 如何复制(或交换)包含引用或const的成员的类型的对象?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在尝试解决的问题是产生容器,例如包含引用和const数据成员的对象的std ::向量:
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没有.

猜你在找的C&C++相关文章