这篇文章有点啰嗦,所以在我开始之前,我想弄清楚我在问什么:你是否在你的代码中添加了启用移动的设置器并且你发现它值得付出努力?我发现有多少行为可能是特定于编译器的?
我在这里看到的是在我设置复杂类型的属性的情况下是否值得添加启用移动的 setter 函数。在这里,我启用了移动Bar
,并且Foo
有一个Bar
可以设置的属性。
class Bar {
public:
Bar() : _array(1000) {}
Bar(Bar const & other) : _array(other._array) {}
Bar(Bar && other) : _array(std::move(other._array)) {}
Bar & operator=(Bar const & other) {
_array = other._array;
return *this;
}
Bar & operator=(Bar && other) {
_array = std::move(other._array);
return *this;
}
private:
vector<string> _array;
};
class Foo {
public:
void SetBarByCopy(Bar value) {
_bar = value;
}
void SetBarByMovedCopy(Bar value) {
_bar = std::move(value);
}
void SetBarByConstRef(Bar const & value) {
_bar = value;
}
void SetBarByMove(Bar && value) {
_bar = std::move(value);
}
private:
Bar _bar;
};
一般来说,过去我为非内置类型的 setter 函数使用了 const-ref。我看到的选项是按值传递然后移动(SetByMovedCopy
),通过 const-ref 然后复制(SetByConstRef
),最后通过 r-value-ref 接受然后移动(SetByMove
)。作为基线,我还包括按值传递然后复制(SetByCopy
)。FWIW,如果同时包含按值传递和 r-value-ref 重载,编译器会抱怨模棱两可。
在使用 VS2010 编译器进行实验时,我发现:
Foo foo;
Bar bar_one;
foo.SetByCopy(bar_one);
// Bar::copy ctor called (to construct "value" from bar_one)
// Foo::SetByCopy entered
// Bar::copy operator= called (to copy "value" to _bar)
// Foo::SetByCopy exiting
// Bar::dtor called (on "value")
value
从 复制构造bar_one
,然后value
复制到bar
。value
被破坏并产生破坏完整对象的任何成本。执行 2 次复制操作。
foo.SetByMovedCopy(bar_one);
// Bar::copy ctor called (to construct "value" from bar_one)
// Foo::SetByCopy entered
// Bar::move operator= called (to move "value" into _bar)
// Foo::SetByCopy exiting
// Bar::dtor called (to destruct the moved "value")
value
从 复制构造bar_one
,然后value
移入_bar
,然后在函数退出后破坏内脏value
,大概成本较低。1 复制和 1 移动操作。
foo.SetByConstRef(bar_one);
// Foo::SetByConstRef entered
// Bar::copy operator= called (to copy bar_one into _bar)
// Foo::SetByConstRef exiting
bar_one
直接复制到_bar
. 1复制操作。
foo.SetByMove(std::move(bar_one))
// Foo::SetByMove entered
// Bar::move operator= called (to move "value" into _bar)
// Foo::SetByMove exited
bar_one
直接移入_bar
. 1 移动操作。
所以 const-ref 和 move 版本在这种情况下是最有效的。现在,更重要的是,我想要做的是这样的事情:
void SetBar(Bar const & value) { _bar = value; }
void SetBar(Bar && value) { _bar = std::move(value); }
我在这里发现的是,如果您调用Foo::SetBar
,编译器会根据您传递的是左值还是右值来选择函数。您可以通过这样调用来强制解决问题std::move
:
foo.SetBar(bar_one); // Const-ref version called
foo.SetBar(Bar()); // Move version called
foo.SetBar(std::move(bar_one)); // Move version called
想到添加所有这些移动设置器,我不寒而栗,但我认为在将临时传递给函数的情况下,它可能会带来相当显着的性能提升,并且我可以通过在适当SetBar
的地方应用来获得更多收益。std::move