【C++】 5_C++11 新特性-標準庫篇

Rvalue references

Rvalue references are a new reference type introduced in C++0x that help solver the problem of unnecessary copying and enable perfect forwarding. When the right-hand side of an assignment is an rvalue, then the left-hand side object can 'steal' resource from the right-hand side object rather than performing a separate allocation, thus enabing move semantics.

(右值引用是C ++ 0x中引入的新引用類型,可幫助解決沒必要要的複製問題並實現完美轉發。 當賦值的右側是右值時,則左側對象能夠從右側對象竊取資源,而沒必要執行單獨的分配,從而能夠實現移動語義。)ios

  • Lvalue : 能夠出如今 operator= 左側、右側
  • Rvalue : 只能出如今 operator= 右側

以 int 測試:c++

int a = 9;
int b = 4;

a = b;       // OK
b = a;       // OK
a = a + b;   // Ok

a+b = 42;    // error: lvalue required as left operand of assignment

以 string 測試:ide

string s1("Hello ");   
string s2("Word!");

s1 + s2 = s2;      // 居然編譯經過!! 
string() = "Word"; // 居然能夠對 temp object 賦值!!

以 complex 測試:函數

complex<int> c1(3, 8);
complex<int> c2(1, 9);

c1 + c2 = c2;     // 居然編譯經過!
complex<int>() = complex<int>(4,9);  // 居然能夠對 temp object 賦值!!

已知 (a+b) 、臨時對象爲右值,只能出如今賦值符號右邊,但 string,complex 的表現與咱們認知不一樣。測試

C++ with its user-defined types has introduced some subtleties regarding modifiability and assignability that cause this definition to be incorrect.

(C ++及其用戶定義類型引入了一些與可修改性和可分配性有關的細微之處,這些細微之處致使該定義不正確。)ui

總結:this

  • 臨時對象是一種右值
  • 右值不能夠放在賦值符號左邊

右值引用只能出如今賦值符號左邊

int foo() 
{
    return 5;
}

int x = foo();    // OK
int *p = &foo();  // error: lvalue required as unary ‘&’ operand
foo() = 7;        // error: lvalue required as left operand of assignment

對 Rvalue 取 reference 不能夠。沒有所謂的 Rvalue reference(c++0x 以前)。spa

當 Rvalue 出如今 operator=(copy assignment) 的右側,咱們認爲對其資源進行「偷取/搬移(move)」而非拷貝(copy)是能夠的,合理的。
那麼:指針

  1. 必須有語法讓咱們在調用端告訴編譯器,這是一個 Rvalue。
  2. 必須有語法讓咱們在被調用端寫出一個專門處理 Rvalue 的所謂 move assignment 函數。

Rvalue references and Move Semantics

僞代碼:code

using namespace std;

class MyString
{
private:
    char  *_data;

public:
    // copy ctor
    MyString(const MyString &str) : initializer_list
    {
        // ... 
    }   

    // move cpor
    MyString(const MyString &&str) noexcept : initializer_list  // 注意這裏 !!
    {
        // ...
    }
    
    // copy assignment
    MyString& operator=(const MyString &str)
    {
        return *this;
    }
    
    // move assignment
    MyString& operator=(const MyString &&str) noexcept          // 注意這裏 !!
    {
        return *this;
    }
};

template <typename M>
void test_moveable(M c, long  value)
{
    typedef typename iterator_traits<typename M::iterator>::value_type Vtype;

    char buf[10] = {0};

    clock_t timeStart = clock();

    for (long i=0; i<value; ++i)
    {
        snprintf(buf, 10, "%d", rand());    // 隨即數轉爲字符串
        auto ite = c.end();                 // 定位到尾部
        c.insert(ite, Vtype(buf));          // 插入        注意這裏!!
        output_static_data(*(c.begin()));   // copy/move ctor 被調用次數
    }

    cout << "millo - second : " << (clock() - timeStart) << endl;
    cout << "size()  = " << c.size() << endl;

    M c1(c);                // copy ctor
    M c2(std::move(c1));    // move ctor    注意這裏!! (必須確保之後再也不使用c1,由於c1 源數據被偷走)

    c1.swap(c2);
}

說明:

  • vector中的兩個insert版本
iterator insert(const_iterator __position, const value_type& __x);

iterator insert(const_iterator __position, value_type&& __x)
    { return _M_insert_rval(__position, std::move(__x)); }
  • M c2(std::move(c1));
    std::move函數能夠強制將左值引用轉換爲右值引用。 但須要使用者保證必須確保之後再也不使用c1,由於c1 源數據被偷走。
  • copy and move 圖解

具體發生在指針上

image.png

-
image.png

  • noexcept
You need to inform C++ (specifically std::vector) that your move constructotr and destructor does not throw, Then the move constructor will be called when the vector grows.

(你須要通知C ++(特別是std :: vector),你的move構造函數和析構函數不會拋出異常,而後,當vector增大時,將調用移動構造函數。)

Unperfect Forwarding

文件:Test.cpp

#include <iostream>

using namespace std;

void process(int &i)
{
    cout << "process(int&): " << i << endl;
}

void process(int &&i)
{
    cout << "void process(int &&i): " << i << endl;
}

void forward(int &&i)
{
    cout << "forward(int &&i): " << i << endl;
    process(i);
}

int main()
{
    int a = 0;

    process(a);
    process(1);
    process(move(a));

    cout << "**************" << endl;

    forward(2);             // 注意這裏輸出!! 右值引用在數據傳遞時丟失了信息
    forward(move(a));       // 注意這裏輸出!! 右值引用在數據傳遞時丟失了信息
    // forward(a);          // error: cannot bind rvalue reference of type ‘int&&’ to lvalue of type ‘int’

    return 0;
}

輸出:

process(int&): 0
void process(int &&i): 1
void process(int &&i): 0
**************
forward(int &&i): 2
process(int&): 2
forward(int &&i): 0
process(int&): 0

任何的函數內部,對形參直接使用,都會被按照左值進行處理。

Perfect Forwarding

Perfect forwarding allows you to write a single function template that takes n arbitrary arguments and forwards them transparently to another arbitrary function. The nature of the argument(modifiable, const, lvalue or rvalue) is preserved in this forwarding process.
(完美轉發使您能夠編寫一個帶有n個任意參數的函數模板,並將其透明地轉發給另外一個任意函數。 在此轉發過程當中保留了參數的性質(可修改,const,左值或右值)。)
template <typename T1, typename T2>
void functionA(T1 &&t1, T2 &&t2)
{
    functionB(std::forward<T1>(t1), std::forward<T2>(t2));
}

文件:Test.cpp

#include <iostream>

using std::cout;
using std::endl;
using std::move;

void process(int &i)
{
    cout << "process(int&): " << i << endl;
}

void process(int &&i)
{
    cout << "void process(int &&i): " << i << endl;
}

template <typename T>
void forward(T &&i)
{
    cout << "forward(T &&i): " << i << endl;
    process(std::forward<T>(i));
}

int main()
{
    int a = 0;

    process(a);
    process(1);
    process(move(a));

    cout << "**************" << endl;

   forward(2);             // 注意這裏輸出!!
   forward(move(a));       // 注意這裏輸出!! 

    return 0;
}

輸出:

process(int&): 0
void process(int &&i): 1
void process(int &&i): 0
**************
forward(T &&i): 2
void process(int &&i): 2
forward(T &&i): 0
void process(int &&i): 0

文件:move.h

/**
*  @brief  Forward an lvalue.
*  @return The parameter cast to the specified type.
*
*  This function is used to implement "perfect forwarding".
*/
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); }

/**
*  @brief  Forward an rvalue.
*  @return The parameter cast to the specified type.
*
*  This function is used to implement "perfect forwarding".
*/
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
{
  static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
        " substituting _Tp is an lvalue reference type");
  return static_cast<_Tp&&>(__t);
}

//-----------------

/**
*  @brief  Convert a value to an rvalue.
*  @param  __t  A thing of arbitrary type.
*  @return The parameter cast to an rvalue-reference to allow moving it.
*/
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

// ----------------

/// remove_reference
template<typename _Tp>
struct remove_reference
{ typedef _Tp   type; };

template<typename _Tp>
struct remove_reference<_Tp&>
{ typedef _Tp   type; };

template<typename _Tp>
struct remove_reference<_Tp&&>
{ typedef _Tp   type; };
相關文章
相關標籤/搜索