zoukankan      html  css  js  c++  java
  • 第6章 移动语义和enable_if:6.1 完美转发

    Chapter 6: Move Semantics and enable_if<>

    第6章 移动语义和enable_if<>

    One of the most prominent features C++11 introduced was move semantics. You can use it to optimize copying and assignments by moving (“stealing”) internal resources from a source object to a destination object instead of copying those contents. This can be done provided the source no longer needs its internal value or state (because it is about to be discarded).

    移动语义是C++11引入的最突出的特性之一。通过移动语义可以优化复制和赋值操作:将源对象的内部资源move(“窃取”)到目标对象,而不是复制它们的内容。这样做的前提是,源对象不再需要其内部值或状态(因为源对象将被丢弃)。

    Move semantics has a significant influence on the design of templates, and special rules were introduced to support move semantics in generic code. This chapter introduces these features.

    移动语义对模板的设计有着重要影响,在泛型代码中引入了一些特殊的规则来支持移动语义。本章介绍这些特性。

    6.1 Perfect Forwarding

    6.1 完美转发

    Suppose you want to write generic code that forwards the basic property of passed arguments:

    假如你希望编写泛型代码来实现将传入参数的基本属性转发出去:

      • Modifyable objects should be forwarded so that they still can be modified.

       可修改对象被转发后依然是可修改的。

      • Constant objects should be forwarded as read-only objects.

        const对象只能被转发为只读对象。

      • Movable objects (objects we can “steal” from because they are about to expire) should be forwarded as movable objects.

        可移动对象(可以从中窃取资源的对象,因为它们即将过期)被转发后仍然是可移动的。

    To achieve this functionality without templates, we have to program all three cases.

    不使用模板的话,为了达到这一目的,就需要对以上三种情况分别进行编程。

    For example, to forward a call of f() to a corresponding function g():

    例如,为了将调用f()时传递的参数转发给相应的g()函数:

    #include <utility>
    #include <iostream>
    
    class X {
        ...
    };
    
    void g(X&) {
        std::cout << "g() for variable
    ";
    }
    
    void g(X const&) {
        std::cout << "g() for constant
    ";
    }
    
    void g(X&&) {
        std::cout << "g() for movable object
    ";
    }
    
    // let f() forward argument val to g():
    void f(X& val) {
        g(val); // val is non-const lvalue => calls g(X&)
    }
    
    void f(X const& val) {
        g(val); // val is const lvalue => calls g(X const&)
    }
    
    void f(X&& val) {
        g(std::move(val)); // val is non-const lvalue => needs std::move() to call g(X&&)
    }
    
    int main()
    {
        X v; // create variable
        X const c; // create constant
    
        f(v); // f() for nonconstant object calls f(X&) => calls g(X&)
        f(c); // f() for constant object calls f(X const&) => calls g(X const&)
        f(X()); // f() for temporary calls f(X&&) => calls g(X&&)
        f(std::move(v)); // f() for movable variable calls f(X&&) => calls g(X&&)
    }

    Here, we see three different implementations of f() forwarding its argument to g():

    这里定义了三个不同的f()函数,它们将分别将其参数转发给g()函数:

    void f(X& val) {
        g(val); // val 是一个非const的左值 =>  calls g(X&)
    }
    
    void f(X const& val) {
        g(val); // val是一个const的左值 => calls g(X const&)
    }
    
    void f(X&& val) {
        g(std::move(val)); // val是一个非const的左值 => 需要使用std::move来调用 g(X&&)
    }

    Note that the code for movable objects (via an rvalue reference) differs from the other code: It needs a std::move() because according to language rules, move semantics is not passed through. Although val in the third f() is declared as rvalue reference its value category when used as expression is a nonconstant lvalue (see Appendix B) and behaves as val in the first f(). Without the move(), g(X&) for nonconstant lvalues instead of g(&&) would be called.

    注意,其中针对可移动对象的那段代码(通过右值引用)不同于其他代码:它需要调用std::move(),因为根据语言规则,移动语义是不会被传递的。虽然在第3个f()函数中,val被声明为右值引用,但是当作为表达式使用时,它的值类型是一个非const的左值(见附录B),其行为与第1个f()函数是一样的。因此,如果不使用move(),则会调用非const左值版本的g(X&),而不是g(&&)函数。

    If we want to combine all three cases in generic code, we have a problem:

    如果我们想要在泛型代码统一以上三种情况,会遇到一个问题:

    template<typename T>
    void f(T val) {
        g(val);
    }

    works for the first two cases, but not for the (third) case where movable objects are passed.

    这个模板只对前两种情况有效,但对于第3种可移动对象是无效的。

    C++11 for this reason introduces special rules for perfect forwarding parameters. The idiomatic code pattern to achieve this is as follows:

    因此,C++11引入特殊的规则对参数进行完美转发。实现这一目标的惯用代码模式如下:

    template<typename T>
    void f(T&& val) {
        g(std::forward<T>(val)); // 将val完美转发给g()
    }

    Note that std::move() has no template parameter and “triggers” move semantics for the passed argument, while std::forward<>() “forwards” potential move semantic depending on a passed template argument.

    注意:std::move()是不带模板参数的,它会无条件地移动其参数。而std::forward<>会根据被传递参数的具体情况决定是否“转发”其潜在的移动语义。

    Don’t assume that T&& for a template parameter T behaves as X&& for a specific type X. Different rules apply! However, syntactically they look identical:

    不要以为模板参数T的T&&和具体类型X的X&&是一样的。虽然语法上看上去相同,但是它们适用于不同的规则:

      • X&& for a specific type X declares a parameter to be an rvalue reference. It can only be bound to a movable object (a prvalue, such as a temporary object, and an xvalue, such as an object passed with std::move(); see Appendix B for details). It is always mutable and you can always “steal” its value.

      具体类型X的X&&声明了一个右值引用。只能被绑定到一个移动对象上(prvalue,如临时对象。Xvalue,如通过std::move()传递的对象。更多细节见附录B)。它的值是可变的,而是总是可以被“窃取”。

      • T&& for a template parameter T declares a forwarding reference (also called universal reference).  It can be bound to a mutable, immutable (i.e., const), or movable object. Inside the function definition, the parameter may be mutable,immutable, or refer to a value you can “steal” the internals from.

      模板参数T的T&&声明了一个转发引用(也称为万能引用)。可以被绑定到可变、不可变(如const)或可移动对象上。在函数内部的定义中,这个参数也是可变的、不可变或者指向一个可以被“窃取”内部数据的值。

    Note that T must really be the name of a template parameter. Depending on a template parameter is not sufficient. For a template parameter T, a declaration such as typename T::iterator&& is just an rvalue reference, not a forwarding reference.

    注意T必须是模板参数的名字。只是依赖于模板参数是不够的。例如对于模板参数T,typname T::iterator&&只是声明了一个右值引用,并不是一个转发引用。

    So, the whole program to perfect forward arguments will look as follows:

    因此,整个完美转发参数的程序会像下面这样:

    include <utility>
    #include <iostream>
    
    class X {
        ...
    };
    
    void g(X&) {
        std::cout << "g() for variable
    ";
    }
    
    void g(X const&) {
        std::cout << "g() for constant
    ";
    }
    
    void g(X&&) {
        std::cout << "g() for movable object
    ";
    }
    
    // let f() perfect forward argument val to g():
    template<typename T>
    void f(T&& val) {
        g(std::forward<T>(val)); // 对于任何val参数都可调用正确的 g() 函数。
    }
    
    int main()
    {
        X v; // create variable
        X const c; // create constant
    
        f(v); // f() for variable calls f(X&) => calls g(X&)
        f(c); // f() for constant calls f(X const&) => calls g(X const&)
        f(X()); // f() for temporary calls f(X&&) => calls g(X&&)
    
        f(std::move(v)); // f() for move-enabled variable calls f(X&&)=> calls g(X&&)
    }

    Of course, perfect forwarding can also be used with variadic templates (see Section 4.3 on page 60 for some examples). See Section 15.6.3 on page 280 for details of perfect forwarding.

    当然,完美转发也可以应用于变量模板(更多例子见第60页4.3节)。更多关于完美转发的细节请参阅第280页15.6.3节。

  • 相关阅读:
    NOIP 2012 文化之旅
    史上最全的各种C++ STL容器全解析
    详解C++ STL map 容器
    详解C++ STL priority_queue 容器
    浅谈C++ STL stack 容器
    浅谈C++ STL queue 容器
    浅谈C++ STL vector 容器
    CF1185F Two Pizzas
    浅谈C++ STL deque 容器
    详解C++ STL multiset 容器
  • 原文地址:https://www.cnblogs.com/5iedu/p/12761344.html
Copyright © 2011-2022 走看看