Should I always move on `sink` constructor or setter arguments?

自作多情 提交于 2019-11-28 18:47:05

The simple answer is: yes.


The reason is quite simple as well, if you store by value you might either need to move (from a temporary) or make a copy (from a l-value). Let us examine what happens in both situations, with both ways.

From a temporary

  • if you take the argument by const-ref, the temporary is bound to the const-ref and cannot be moved from again, thus you end up making a (useless) copy.
  • if you take the argument by value, the value is initialized from the temporary (moving), and then you yourself move from the argument, thus no copy is made.

One limitation: a class without an efficient move-constructor (such as std::array<T, N>) because then you did two copies instead of one.

From a l-value (or const temporary, but who would do that...)

  • if you take the argument by const-ref, nothing happens there, and then you copy it (cannot move from it), thus a single copy is made.
  • if you take the argument by value, you copy it in the argument and then move from it, thus a single copy is made.

One limitation: the same... classes for which moving is akin to copying.

So, the simple answer is that in most cases, by using a sink you avoid unnecessary copies (replacing them by moves).

The single limitation is classes for which the move constructor is as expensive (or near as expensive) as the copy constructor; in which case having two moves instead of one copy is "worst". Thankfully, such classes are rare (arrays are one case).

A bit late, as this question already has an accepted answer, but anyways... here's an alternative:

struct Test {
    std::string str;
    Test(std::string&& mStr) : str{std::move(mStr)} { } // 1
    Test(const std::string& mStr) : str{mStr} { } // 2
};

Why would that be better? Consider the two cases:

From a temporary (case // 1)

Only one move-constructor is called for str.

From an l-value (case // 2)

Only one copy-constructor is called for str.

It probably can't get any better than that.

But wait, there is more:

No additional code is generated on the caller's side! The calling of the copy- or move-constructor (which might be inlined or not) can now live in the implementation of the called function (here: Test::Test) and therefore only a single copy of that code is required. If you use by-value parameter passing, the caller is responsible for producing the object that is passed to the function. This might add up in large projects and I try to avoid it if possible.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!