如何复制(或交换)包含引用或const的成员的类型的对象? [英] How to copy (or swap) objects of a type that contains members that are references or const?
问题描述
我想要解决的问题是使用包含引用和const数据成员的对象的 std :: vector
容器:
struct Foo;
struct Bar {
Bar(Foo& foo,int num):foo_reference(foo),number(num){}
private:
Foo& foo_reference;
const int number;
//可变成员数据省略
};
struct Baz {
std :: vector< Bar> bar_vector;
};
这不会工作,因为类的默认赋值运算符,由于引用成员
。 foo_reference
和const成员 number
,无法构建Foo
一个解决方案是将 foo_reference
改为指针,并除去 const
关键字。然而,这丢失了对指针的引用的优点,并且 const
成员应该是 const
。他们是私人成员,所以唯一可以伤害的是我自己的代码,但我已经用自己的代码在脚(或更高)自拍。
我已经看到这个问题的解决方案在Web上的 swap
方法,似乎充满了未定义的行为基于 reinterpret_cast
和 const_cast
。它发生,这些技术似乎在我的电脑上工作。今天。有一个特定版本的一个特定的编译器。明天,还是用不同的编译器?谁知道。
关于stackoverflow的相关答案:
- C ++:在带有* all * const数据成员的类中实现复制赋值运算符。这是有意义的吗?
第一个答案有一个有趣的线如果那个不可变,你的错误。 - 使用const成员的Swap方法
第一个答案在此处并不适用,
因此,有一种方法可以写一个 swap $
编辑
>
为了清楚起见,我已经非常清楚这个解决方案:
struct Bar {
Bar(Foo& foo,int num):foo_ptr(& foo),number(num){}
private:
Foo * foo_ptr;
int number;
//可变成员数据省略
};
这显式地消除了 const
code> number ,并且消除了 foo_reference
的隐含 const
这不是我后来的解决方案。如果这是唯一的非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));
}
,然后使用复制和交换写入赋值运算符。这绕过了参考和const问题,但它是UB? (至少它不使用 reinterpret_cast
和 const_cast
。)一些隐藏的可变数据是包含 std :: vector
s,所以我不知道这样的浅层复制是否会在这里工作。
如果你用移动操作符实现这个方法有一种方法:
Bar :: operator =(Bar& source){
this - > 〜Bar();
new(this)Bar(std :: move(source));
return * this;
}
你不应该真正使用这个技巧与复制构造函数,因为他们可以抛出然后这是不安全的。移动构造函数不应该永远抛出,所以这应该是确定的。
std :: vector
和其他容器现在尽可能利用移动操作,所以调整大小和排序等等。
这种方法将允许你保持const和引用成员,但你仍然无法 复制对象。要做到这一点,你必须使用非常量和指针成员。
顺便说一句,你不应该使用像非POD类型的memcpy。 / p>
编辑
对未定义行为投诉的回复。
问题情况似乎是
struct X {
const int&会员;
X& operator =(X&&){... as above ...}
...
};
X x;
const int& foo = xmember;
X = std :: move(some_other_X);
// foo不再有效
如果继续使用, foo
。对我来说,这与
X * x = new X();
const int& foo = xmember;
delete x;
其中很清楚使用 foo
无效。
也许对 X :: operator =(X&&&)
导致你认为 foo
在移动后仍然有效,有点像这样
const int& (X :: * ptr)=& X :: member;
X x;
// x。* ptr是x.member
X = std :: move(some_other_X);
// x。* ptr is STILL x.member
成员指针 ptr
不会移动 x
,但 foo
>
The problem I am trying to address arises with making containers such as an std::vector
of objects that contain reference and const data members:
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;
};
This won't work as-is because the default assignment operator for class Foo
can't be built due to the reference member foo_reference
and const member number
.
One solution is to change that foo_reference
to a pointer and get rid of the const
keyword. This however loses the advantages of references over pointers, and that const
member really should be const
. They are private members, so the only thing that can do harm is my own code, but I have shot myself in the foot (or higher) with my own code.
I've seen solutions to this problem on the web in the form of swap
methods that appear to be chock full of undefined behavior based on the wonders of reinterpret_cast
and const_cast
. It happens that those techniques do appear to work on my computer. Today. With one particular version of one particular compiler. Tomorrow, or with a different compiler? Who knows. I am not going to use a solution that relies on undefined behavior.
Related answers on stackoverflow:
- C++ : Implement the copy-assignment operator in a class with *all* const data members. Does this make sense?
The first answer has the amusing line "If that one is immutable, your screwed." - Swap method with const members
The first answer doesn't really apply here, and the second is a bit of a kludge.
So is there a way to write a swap
method / copy constructor for such a class that does not invoke undefined behavior, or am I just screwed?
Edit
Just to make it clear, I already am quite aware of this solution:
struct Bar {
Bar (Foo & foo, int num) : foo_ptr(&foo), number(num) {}
private:
Foo * foo_ptr;
int number;
// Mutable member data elided
};
This explicitly eliminates the const
ness of number
and the eliminates the implied const
ness of foo_reference
. This is not the solution I am after. If this is the only non-UB solution, so be it. I am also quite aware of this solution:
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));
}
and then writing the assignment operator using copy-and-swap. This gets around the reference and const problems, but is it UB? (At least it doesn't use reinterpret_cast
and const_cast
.) Some of the elided mutable data are objects that contain std::vector
s, so I don't know if a shallow copy like this will work here.
If you implement this with move operators there is a way:
Bar & Bar :: operator = (Bar && source) {
this -> ~ Bar ();
new (this) Bar (std :: move (source));
return *this;
}
You shouldn't really use this trick with copy constructors because they can often throw and then this isn't safe. Move constructors should never ever throw, so this should be OK.
std::vector
and other containers now exploit move operations wherever possible, so resize and sort and so on will be OK.
This approach will let you keep const and reference members but you still can't copy the object. To do that, you would have to use non-const and pointer members.
And by the way, you should never use memcpy like that for non-POD types.
Edit
A response to the Undefined Behaviour complaint.
The problem case seems to be
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
True it is undefined behaviour if you continue to use foo
. To me this is the same as
X * x = new X ();
const int & foo = x.member;
delete x;
in which it is quite clear that using foo
is invalid.
Perhaps a naive read of the X::operator=(X&&)
would lead you to think that perhaps foo
is still valid after a move, a bit like this
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
The member pointer ptr
survives the move of x
but foo
does not.
这篇关于如何复制(或交换)包含引用或const的成员的类型的对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!