我们需要移动和复制作业吗
Do we need move and copy assignment
对于class A
,我们可以使用
A& operator=( A other ) { /* ... */ }
而不是
A& operator=( const A& other ) { /* ... */ }
A& operator=( const A&& other ) { /* ... */ }
不会降低性能或产生其他负面影响?
实际上,您可以使用
实现复制和交换
A& operator=( A other ) { swap(other, *this); return *this; }
对于复制和移动赋值,但自赋值运算符的无条件复制会降低性能。
除此之外,上面的功能不能标注noexcept
, move应该是轻量级的,廉价的,不是必需的,但它应该是轻量级的。因此,移动 constructor/assignment 应该是 noexcept
。没有noexcept
move constructor/assignment operator, move_if_noexcept
容器增长时无法调用,容器会回去copy.
因此,推荐的方式是:
A& operator=( const A& other ) {
if (std::addressof(other) != this) {
A(other).swap(*this); // Copy constructor may throw
}
return *this;
}
A& operator=( A&& other ) noexcept { // note no const here
// we don't check self assignment because
// an object binds to an rvalue reference it is one of two things:
// - A temporary.
// - An object the caller wants you to believe is a temporary.
swap(other);
return *this;
}
without worsening performance or other negative effects?
依赖A
的数据成员和基类,甚至可以依赖A的基和成员的基和成员
这是视频的一部分 link,它演示了如果 A
有一个 vector
作为数据成员,那么 copy/swap 习语可以花费复制赋值运算符 70% 的性能命中率 平均 。这与默认分配特殊成员的更简单方法相比。
http://www.youtube.com/watch?v=vLinb2fgkHk&t=35m30s
class A
{
std::vector<int> v_;
public:
// ...
};
您可以预期 string
个数据成员具有与 vector
个数据成员相同的影响。
如果您不确定 A
,并且性能对您很重要,请尝试两种方式并进行衡量。这正是视频中演示的内容。
"move" 上下文的性能可能会受到轻微影响。
如果您的 class A
是可移动的,那么在移动上下文中,按值传递复制和交换实现会将移动实现为两个操作的序列:
- 使用
A
的移动构造函数将右侧 (RHS) 值移动到参数 other
。
- 将数据从
other
移动到 *this
(或将 other
替换为 *this
)。
第二个实现(使用专门的移动赋值)可以在一个操作中完成同样的事情:它会直接移动(或交换)右边的值 *this
。
因此,复制和交换变体可能会涉及一次额外移动的开销。
"copy" 上下文的性能可能会受到轻微或严重的影响。
如果您的左侧 (LHS) 有一些可复制的内部资源(如内存块)可以容纳相应的 RHS 值而无需重新分配,那么复制分配的专用实现只需要从复制数据RHS 资源到 LHS 资源。不需要内存分配。
在复制和交换变体中,副本是无条件创建的,并且必须无条件地分配和释放上述内部资源。这会对性能产生重大负面影响。
对于class A
,我们可以使用
A& operator=( A other ) { /* ... */ }
而不是
A& operator=( const A& other ) { /* ... */ }
A& operator=( const A&& other ) { /* ... */ }
不会降低性能或产生其他负面影响?
实际上,您可以使用
实现复制和交换A& operator=( A other ) { swap(other, *this); return *this; }
对于复制和移动赋值,但自赋值运算符的无条件复制会降低性能。
除此之外,上面的功能不能标注 move应该是轻量级的,廉价的,不是必需的,但它应该是轻量级的。因此,移动 constructor/assignment 应该是 noexcept
,noexcept
。没有noexcept
move constructor/assignment operator, move_if_noexcept
容器增长时无法调用,容器会回去copy.
因此,推荐的方式是:
A& operator=( const A& other ) {
if (std::addressof(other) != this) {
A(other).swap(*this); // Copy constructor may throw
}
return *this;
}
A& operator=( A&& other ) noexcept { // note no const here
// we don't check self assignment because
// an object binds to an rvalue reference it is one of two things:
// - A temporary.
// - An object the caller wants you to believe is a temporary.
swap(other);
return *this;
}
without worsening performance or other negative effects?
依赖A
的数据成员和基类,甚至可以依赖A的基和成员的基和成员
这是视频的一部分 link,它演示了如果 A
有一个 vector
作为数据成员,那么 copy/swap 习语可以花费复制赋值运算符 70% 的性能命中率 平均 。这与默认分配特殊成员的更简单方法相比。
http://www.youtube.com/watch?v=vLinb2fgkHk&t=35m30s
class A
{
std::vector<int> v_;
public:
// ...
};
您可以预期 string
个数据成员具有与 vector
个数据成员相同的影响。
如果您不确定 A
,并且性能对您很重要,请尝试两种方式并进行衡量。这正是视频中演示的内容。
"move" 上下文的性能可能会受到轻微影响。
如果您的 class
A
是可移动的,那么在移动上下文中,按值传递复制和交换实现会将移动实现为两个操作的序列:- 使用
A
的移动构造函数将右侧 (RHS) 值移动到参数other
。 - 将数据从
other
移动到*this
(或将other
替换为*this
)。
第二个实现(使用专门的移动赋值)可以在一个操作中完成同样的事情:它会直接移动(或交换)右边的值
*this
。因此,复制和交换变体可能会涉及一次额外移动的开销。
- 使用
"copy" 上下文的性能可能会受到轻微或严重的影响。
如果您的左侧 (LHS) 有一些可复制的内部资源(如内存块)可以容纳相应的 RHS 值而无需重新分配,那么复制分配的专用实现只需要从复制数据RHS 资源到 LHS 资源。不需要内存分配。
在复制和交换变体中,副本是无条件创建的,并且必须无条件地分配和释放上述内部资源。这会对性能产生重大负面影响。