我在VS2013上编译了以下代码(使用“发布”模式优化),但沮丧地发现的程序集与std::swap(v1,v2)
并不相同std::swap(v3,v4)
。
#include <vector>
#include <iterator>
#include <algorithm>
template <class T>
class WRAPPED_VEC
{
public:
typedef T value_type;
void push_back(T value) { m_vec.push_back(value); }
WRAPPED_VEC() = default;
WRAPPED_VEC(WRAPPED_VEC&& other) : m_vec(std::move(other.m_vec)) {}
WRAPPED_VEC& operator =(WRAPPED_VEC&& other)
{
m_vec = std::move(other.m_vec);
return *this;
}
private:
std::vector<T> m_vec;
};
int main (int, char *[])
{
WRAPPED_VEC<int> v1, v2;
std::generate_n(std::back_inserter(v1), 10, std::rand);
std::generate_n(std::back_inserter(v2), 10, std::rand);
std::swap(v1, v2);
std::vector<int> v3, v4;
std::generate_n(std::back_inserter(v3), 10, std::rand);
std::generate_n(std::back_inserter(v4), 10, std::rand);
std::swap(v3, v4);
return 0;
}
该std::swap(v3, v4)
语句变为“完美”的程序集。我如何才能达到相同的效率std::swap(v1, v2)
?
这里有几点要说明。
1.如果您不确定您的调用方式swap
是否等同于“正确”的调用方式swap
,则应始终使用“正确”的方式:
using std::swap;
swap(v1, v2);
2.在程序swap
集中查找诸如调用之类的一种非常方便的方法是将调用本身置于测试函数中。这样可以很容易地隔离程序集:
void
test1(WRAPPED_VEC<int>& v1, WRAPPED_VEC<int>& v2)
{
using std::swap;
swap(v1, v2);
}
void
test2(std::vector<int>& v1, std::vector<int>& v2)
{
using std::swap;
swap(v1, v2);
}
就目前而言,test1
将调用std::swap
如下所示的内容:
template <class T>
inline
swap(T& x, T& y) noexcept(is_nothrow_move_constructible<T>::value &&
is_nothrow_move_assignable<T>::value)
{
T t(std::move(x));
x = std::move(y);
y = std::move(t);
}
而且速度很快。它将使用WRAPPED_VEC
的move构造函数和move赋值运算符。
但是vector
swap甚至更快:它交换vector
的3个指针,并且如果std::allocator_traits<std::vector<T>::allocator_type>::propagate_on_container_swap::value
为true(不是),那么还将交换分配器。如果它是错误的(并且是),并且两个分配器相等(并且它们是),那么一切正常。否则会发生未定义的行为。
要使性能test1
与test2
性能相同,您需要:
friend
void
swap(WRAPPED_VEC<int>& v1, WRAPPED_VEC<int>& v2)
{
using std::swap;
swap(v1.m_vec, v2.m_vec);
}
需要指出的一件事是:
就您而言,在您始终使用的情况下,std::allocator<T>
该friend
功能始终是一个胜利。但是,如果您的代码允许其他分配器(可能具有状态的分配器,它们可能比较不相等,并且可能具有propagate_on_container_swap::value
false std::allocator<T>
)(那么也是如此),则swap
for的这两种实现会WRAPPED_VEC
有所不同:
1.如果您依赖std::swap
,那么您会受到性能上的打击,但是您将永远不可能陷入不确定的行为。移动构造vector
始终是明确定义的,且为O(1)。移动分配vector
始终是明确定义的,可以是O(1)或O(N),并且可以是noexcept(true)或noexcept(false)。
如果propagate_on_container_move_assignment::value
为false,并且移动分配中涉及的两个分配器不相等,则vector
移动分配将变为O(N)和noexcept(false)。因此,swap
使用vector
移动分配将继承这些特征。但是,无论如何,该行为始终是明确定义的。
2.如果你超载swap
了WRAPPED_VEC
,从而依靠swap
过载vector
,那么你暴露自己的未定义行为的可能性,如果该分配器比较不平等,有propagate_on_container_swap::value
等于假。但是,您获得了潜在的性能胜利。
与往常一样,需要在工程上进行权衡。这篇文章旨在提醒您这些权衡的性质。
PS:以下评论纯属文体风格。所有类别类型的大写名称通常被认为是较差的样式。传统上所有大写名称都保留用于宏。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句