loading...

Move semantic notes

bobfang1992 profile image Bob Fang ・2 min read

From Item 23:

  • std::move cast a value (no matter what kind of value it is) to a rvalue. An approximate implementation is:
//C++ 14
template<typename T>
decltype(auto) move(T&& param)
//C++ 11
template<typename T>
typename remove_reference<T>::type&&
{
    using ReturnType = remove_reference_t(T)&&;
    return static_cast<ReturnType>(param);
}

  • If you want to move anything, don't declare it to be const. Think about how string and vector are moved.

  • Fill in the gap here on how std::forward actually works, but conceptually it is just a cast as well, a conditional cast.

From Item 24:

  • Two examples of universal/forwarding references:
template<typename T>
void f(T&& param); // param is universal reference

auto&& var2 = var1; // var2 is universal reference

Universal references come from type deduction (see examples above).

Why do we need to know this? --> Not sure at this moment.

From Item 25:

Use std::move on rvalue references and std::forward on universal references

The first take away for me here, which seems a bit surprising still, is this example:

class Widget {
    public:
    Widget(Widget&& rhs)
    : name (std::move(rhs.name)),
      p(std::move(rhs.p)
    { ... }
    ...
}

So why do we need std::move for rhs.name? That is because rhs is actually a lvalue, not a rvalue. See my SO question here for a more detailed explanation.

This item actually explained a few rules to apply when you write your code. Let me go through them for you:

  1. use std::move for rvalue reference and std::forward for universal reference respectively, if inside your function there are multiple usages of the reference, you might just want to apply std::move and std::forward for the last usage

  2. If you are returning a rvalue reference passed as a function parameter, the rule above still applies, you have to use std::move or std::forward to achieve the optimal

  3. But if you are returning a local object, don't use std::move because that will invalidate RVO (return value optimisation) for the compiler.

From Item 26 & 27
Avoid overloading on universal references

This is because overloading on universal references can take a very high priority when the compiler tries to resolve overloaded function and thus cause confusion. This is especially true for constructors. But sometimes we cannot avoid this, again, especially for constructors since we have to overload them. In item 27 Scott introduced us some techniques to resolve this issue. I think the most important one is using tag dispatch.

So what is tag dispatch?

<.... revisit item 26 and 27 later ... >

Item 28

This item just states two rules:

  1. Although programmers cannot declare "reference to reference", the compiler is allowed to generate "reference to reference" in certain context.

  2. When we have reference to reference, a rule called reference collapsing comes into play, in short, it states:

If either reference is an lvalue reference, the result is an lvalue ref. Otherwise, it is a rvalue ref.

Discussion

pic
Editor guide