歡迎使用CSDN-markdown編輯器

C++11 特性

Including
Non-throwing swap idiom
Copy-and-swap idiom
Move assignment operator
std::move
const & 常量引用
右值引用


Non-throwing swap idiom

傳統swap無法應對資源情況。這裏採用交換指針。
swap參數爲指針
temp.swap (*this)
http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-throwing_swap

Copy-and-swap idiom

There are at least 3 types of exception safety levels: basic, strong, and no-throw.
http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Copy-and-swap

String & operator = (const String &s)
{
   String temp (s); // Copy-constructor -- RAII
   temp.swap (*this); // Non-throwing swap
   return *this;
}// Old resources released when destructor of temp is called.

T& T::operator=(T arg) 
{                 // copy/move constructor is called to construct arg
    swap(arg);    // resources exchanged between *this and arg
    return *this;
}  // destructor is called to release the resources formerly held by *this

For non-throwing swap(), this form provides strong exception guarantee. For rvalue arguments, this form automatically invokes the move constructor, and is sometimes referred to as “unifying assignment operator” (as in, both copy and move).

 // non-copy-and-swap assignment
   C& operator=(const C& other)

Move assignment operator

Move assignment operators typically “steal” the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc), rather than make copies of them, and leave the argument in some valid but otherwise indeterminate state. For example, move-assigning from astd::string or from a std::vector leaves the right-hand side argument empty.

如果X是一種類型,那麼X&&就叫做X的右值引用
Both

T& T::operator=(const T&&)
T& T::operator=(T&&)

~D() {}; // destructor would prevent implicit move assignment
D& operator=(D&&) = default; // force a move assignment anyway

http://en.cppreference.com/w/cpp/language/move_operator

std::move

std::move obtains an rvalue reference to its argument and converts it to an xvalue.標準庫提供了一個 std::move 函數,用來把一個左值轉換爲一個右值.

// Simple move constructor
   A(A&& arg) : member(std::move(arg.member)) // the expression "arg.member" is lvalue
   {} 
   // Simple move assignment operator
   A& operator=(A&& other) {
        member = std::move(other.member);
        return *this;
   }

   // uses the push_back(const T&) overload, which means 
       // we'll incur the cost of copying str
       v.push_back(str);
   // uses the rvalue reference push_back(T&&) overload, 
       // which means no strings will be copied; instead, the contents
       // of str will be moved into the vector.  This is less
       // expensive, but also means str might now be empty.
       v.push_back(std::move(str));

Notes
If both copy and move assignment operators are provided, overload resolution selects the move assignment if the argument is an rvalue (either prvalue such as a nameless temporary or xvalue such as the result of std::move), and selects the copy assignment if the argument is lvalue (named object or a function/operator returning lvalue reference). If only the copy assignment is provided, all argument categories select it

C++11中的swap函數是這樣的:

template<class T> 
void swap(T& a, T& b) 
{ 
  T tmp(std::move(a));
  a = std::move(b); 
  b = std::move(tmp);
} 

X a, b;
swap(a, b);

我們可以在一些之前不可複製的類型不被允許的情況下,用一些不可複製但是可以移動的類型(unique_ptr)。這樣的類型是可以作爲容器元素的。

const & 常量引用

C++常量引用語法上可以引用一個臨時變量。
關於引用的初始化有兩點值得注意:
(1)當初始化值是一個左值(可以取得地址)時,沒有任何問題;
(2)當初始化值不是一個左值時,則只能對一個const T&(常量引用)賦值。而且這個賦值是有一個過程的:
首先將值隱式轉換到類型T,然後將這個轉換結果存放在一個臨時對象裏,最後用這個臨時對象來初始化這個引用變量。在這種情況下,const T&(常量引用)過程中使用的臨時對象會和const T&(常量引用)共存亡。
例子:

double&  dr  =  1 ; // 錯誤:需要左值  
const double&  cdr  =  1 ; // ok 

第二句實際的過程如下:

double  temp  =  double (1);  
const double&  cdr  =  temp ; 

右值引用 T&&

一個表達式有左值(lvalue)和右值(rvalue)之分,即是否可尋址之分。
一般的變量都屬於左值,而像(1 + 2)這種表達式的結果,則是不可尋址的,屬於右值。
可改變的左值,被 & 綁定;不可尋址的值自然無法改變(或者說,不應該被改變),則會被 const & 綁定。
在C++11裏,表達式可以被分爲3類:左值、右值、xvalue。其中 xvalue 是右值引用表達式的值,它可左可右。
把複雜的概念拋開,xvalue 可以被看做一個臨時值,意思是馬上就會被銷燬的值。
右值引用和左值引用的行爲差不多,但是有幾點不同,最重要的就是函數重載時左值使用左值引用的版本,右值使用右值引用的版本:

void foo(X& x); // 左值引用重載
void foo(X&& x); // 右值引用重載

X x;
X foobar();

foo(x); // 參數是左值,調用foo(X&)
foo(foobar()); // 參數是右值,調用foo(X&&)

這樣的重載只出現在拷貝構造函數和賦值運算符中,以用來實現move語義:

X& X::operator=(X const & rhs); // classical implementation
X& X::operator=(X&& rhs)
{
  // Move semantics: exchange content between this and rhs
  return *this;
}

如果你實現了void foo(X&);,但是沒有實現void foo(X&&);,那麼和以前一樣foo的參數只能是左值。如果實現了void foo(X const &);,但是沒有實現voidfoo(X&&);,仍和以前一樣,foo的參數既可以是左值也可以是右值。唯一能夠區分左值和右值的辦法就是實現void foo(X&&);。最後,如果只實現了實現voidfoo(X&&);,但卻沒有實現void foo(X&);和void foo(X const &);,那麼foo的參數將只能是右值。

右值引用類型既可以被當作左值也可以被當作右值,判斷的標準是,如果它有名字,那就是左值,否則就是右值。

Example:
Base(Base const & rhs); // non-move semantics
Base(Base&& rhs); // move semantics
左值版本的拷貝構造函數很直白:
Derived(Derived const & rhs) 
  : Base(rhs)
{
  // Derived-specific stuff
}
但右值版本的重載卻要仔細研究下
Derived(Derived&& rhs) 
  : Base(rhs) // 錯誤:rhs是個左值
{
  // ...
}

如果像上面這樣寫,調用的永遠是Base的非move語義的拷貝構造函數。因爲rhs有名字,所以它是個左值。但我們想要調用的卻是move語義的拷貝構造函數,所以應該這麼寫:

Derived(Derived&& rhs) 
  : Base(std::move(rhs)) // good, calls Base(Base&& rhs)
{
  // Derived-specific stuff
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章