Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

return std::move(m_field) or return m_field?

I have read in some others posts that move is unnecessary and even deoptimizes mechanisms like NRVO. But it was in most cases about a local field.

Consider this example:

  • A class stores a temporary field after a computation.
  • I want to take this field without copying it ("consuming" it).

In this case, is it appropriate to use std::move on return? Specifically, are these pieces of code equivalent? The second is more "generic", letting the user decide if the result should be moved or not, and the first one is more strict — any call will consume the result.

Option 1


// Option 1

#include <utility>

template<typename T>
class TemporaryResultHolder
{
public:
    void doTask() { result = T(); }

    T getResult() { return std::move(result); }
    // Or return T&& ?

private:
    T result;
};

int main() {
    TemporaryResultHolder<int> holder;
    holder.doTask();
    int res = holder.getResult();

    return 0;
}

Option 2


// Option 2

#include <utility>

template<typename T>
class TemporaryResultHolder
{
public:
    void doTask() { result = T(); }

    T getResult() { return result; }

private:
    T result;
};

int main() {
    TemporaryResultHolder<int> holder;
    holder.doTask();
    int res = std::move(holder.getResult());

    return 0;
}
like image 669
rafoo Avatar asked Mar 03 '23 08:03

rafoo


1 Answers

If you were to return an object with automatic storage, then you should never return with std::move because former is faster in best case, and equal in worst case.

I have read in some others posts that it's unecessarily and even deoptimize mecanisms like NRVO.

And this is the reason. This doesn't however apply to the function in the question, because it doesn't return an object with automatic storage, but rather a member variable. NRVO cannot apply to a member variable, nor does the implicit move of return value.

So, in your example, return std::move(result); does a move, and return result; does a copy. You should use move if the purpose of the function is to move from the member, and copy if the purpose is to copy the member. Note however that moving from a member in a function that is not rvalue qualified is quite unconventional design, and I recommend to avoid it unless you have a good reason to do so. Also note that the copying version can be const qualified, which would make it more generally useful.

P.S. for int that is used in the example, these distinctions are irrelevant. Moving versus coping is only relevant to non-trivially copyable / -movable types.

like image 129
eerorika Avatar answered Mar 11 '23 09:03

eerorika