Move semantics/theory - C++11, 26 of n

来源:互联网 发布:未成年可以淘宝开店吗 编辑:程序博客网 时间:2024/06/01 18:43
  • std:move() unconditionally converts an lvalue or rvalue to an rvalue.
  • std:forward() forwards the argument passed in exactly the same type as it is. May be lvalue or rvalue (perfect forwarding).
  • When overloading the copy constructor and the copy assignment operator of a class for the sake of move semantics, it is very much recommended that do the following. If you don't do both of these things, then there is at least one very common situation where your move semantics will not be applied despite the fact that you would very much expect it: when an std::vector gets resized, you certainly want move semantics to happen when the existing elements of your vector are being relocated to the new memory block. But that won't happen unless both of 1. and 2. below are satisfied.
    • Strive to write your overloads in such a way that they cannot throw exceptions.
    • If succeeded in not throwing exceptions from your overloads, then make sure to advertise that fact using the new noexcept keyword.
  • reference collapsing rules
    • A& & becomes A&  // Note that, "A& &" is different from "A&&"
    • A& && becomes A&
    • A&& & becomes A&
    • A&& && becomes A&&
  • special template argument deduction rule for function templates that take an argument by rvalue reference to a template argument
    template<typename T>
    void foo(T&&);
    1) When foo is called on an lvalue of type A, then T resolves to A& and hence, by the reference collapsing rules above, the argument type effectively becomes A&.
    2) When foo is called on an rvalue of type A, then T resolves to A, and hence the argument type becomes A&&.
    Example:
    template<typename T, typename Arg>
    shared_ptr<T> factory(Arg&& arg)
    {
        return shared_ptr<T>(new T(std::forward<Arg>(arg)));
    }
    template<class S>
    S&& forward(typename remove_reference<S>::type& a) noexcept
    {
        return static_cast<S&&>(a);
    }

    X x;
    factory<A>(x); 
    // After applying the rules above, "arg" resolves to "X&".  We have:
    shared_ptr<A> factory(X& && arg)
    {
        return shared_ptr<A>(new A(std::forward<X&>(arg)));
    }
    X& && forward(remove_reference<X&>::type&a) noexcept
    {
        return static_cast<X& &&>(a);
    }
    After evaluating the remove_reference and applying the reference collapsing rules, it becomes:
    shared_ptr<A> factory(X& arg)
    {
        return shared_ptr<A>(new A(std::forward<X&>(arg)));
    }
    X& forward(X& a) noexcept
    {
        return static_cast<X&>(a);
    }
    Here we see, std::forward perfectly forwards lvalue.

    Let's see how std:forward forwards rvalue.
    X foo();
    factory<A>(foo());
    // After applying the rules above, "arg" resolves to "X".  We have:
    shared_ptr<A> factory(X&& arg)
    {
        return shared_ptr<A>(new A(std::forward<X>(arg)));
    }
    X&& forward(remove_reference<X>::type& a) noexcept
    {
        return static_cast<X&&>(a);
    }
    After evaluating the remove_reference and applying the reference collapsing rules, it becomes:
    shared_ptr<A> factory(X&& arg)
    {
        return shared_ptr<A>(new A(std::forward<X>(arg)));
    }
    X&& forward(X& a) noexcept
    {
        return static_cast<X&&>(a);
    }
  • "remove_reference" in the definition of std::forward is to force us to explicitly specify Arg as the template argument of std::forward
  • std:move implementation
    template<class T>
    typename remove_reference<T>::type&&
    std::move(T&& a) noexcept
    {
        typedef typename remove_reference<T>::type&& RvalRef;
        return static_cast<RvalRef>(a);
    }
    Example:
    X x;
    std:move(x);
    // "x" resolves to type "X&" by the special template deduction rule
    typename remove_reference<X&>::type&&
    std::move(X& && a) noexcept
    {
        typedef typename remove_reference<X&>::type&& RvalRef;
        return static_cast<RvalRef>(a);
    }
    After evaluating the remove_reference and applying the new reference collapsing rules, it becomes
    X&& std::move(X& a) noexcept
    {
        return static_cast<X&&>(a);
    }
    Here we see, std::move has converted lvalue to rvalue.

    Let's see how it works on rvalue.
    X foo();
    std::move(foo());
    // return value of foo() resolves to type "X" by the special template deduction rule
    typename remove_reference<X>::type&&
    std::move(X&& a) noexcept
    {
        typedef typename remove_reference<X>::type&& RvalRef;
        return static_cast<RvalRef>(a);
    }
    After evaluating the remove_reference and applying the new reference collapsing rules, it becomes
    X&& std::move(X&& a) noexcept
    {
        return static_cast<X&&>(a);
    }
  • Implicit move is not always applicable. It is restricted under circumstances [Refer to Standard] and https://groups.google.com/forum/#!topic/comp.lang.c++/1R1igvCYs8o. 
原创粉丝点击