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

本文深入探讨了C++中的std::move与std::forward的使用技巧,特别是如何正确实现移动语义以提高效率,并通过完美转发来保持参数的原始类型特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值