---
# System prepended metadata

title: Effect Modern C++ Part 2

---

# Effect Modern C++ Part 2

[Part 1](https://hackmd.io/@Xps7TehyQ1GqEs4toiKu4w/H148LXKzu)

# Chapter 5. Rvalue References, Move Semantics, and Perfect Forwarding

Move constructor的呼叫時機：
當object被一個rvalue初始化，且該object型態有定義move constructor (或可能被compiler自動產生)，move constructor會被呼叫
1. initialization:
`T a = std::move(b)`或是`T a(std::move(b))`，其中`b`的型態是`T`
2. function argument passing:
`f(std::move(a))`，其中`a`的型態是`T`且`f`參數是`T` (如`void f(T t)`)
3. function return:
在一個回傳值為`T`的function中 (如`T f()`)，`return a;`，其中`a`的型態是`T`

## Item 23: Understand std::move and std::forward

`std::move`與`std::forward`只是做轉型

`std::move`是無條件將型態轉為rvalue reference，如下
```cpp=
// Example of implementation of move
// C++ 11 version
templte<typename T>
typename remove_reference<T>::type&& move(T&& param)
{
    // use remove_reference to ensure that
    // '&&' is applied to a type rather than a reference
    using ReturnType = typename remove_reference<T>::type&&;
    return static_cast<ReturnType>(param);
}

// C++ 14 version
templte<typename T>
decltype(auto) move(T&& param)
{
    // use remove_reference to ensure that
    // '&&' is applied to a type rather than a reference
    using ReturnType = remove_reference_t<T>&&;
    return static_cast<ReturnType>(param);
}
```

使用`std::move`並不代表一定可以move
```cpp=
class Annotation {
public:
    Annotation(const std::string text)
        : value(std::move(text))  // call copy ctor rather than move ctor
    {
        ...
    }
private:
    std::string value;
};
```
1. 由於`text`是`const std::string`，`std::move(text)`將會轉型成`const std::string&&`
2. `std::string`的move ctor的參數是`std::string&&`
3. 由於不能將`const std::string&&`轉成`std::string&&`，無法使用move ctor
4. 可以將`const std::string&&`轉成`const std::string&`，因此使用copy ctor
5. 因此，若是想要將傳進來的參數做move，則參數型態不要宣告成`const`，如例子中，只要`Annotation(std::string)`就可以

`std::forward`是有條件的將型態轉為rvalue reference，通常用在有universal reference的template function上
```cpp=
void process(const Widget& lvalueArg); // accept lvalue ref
void process(Widget&& rvalueArg);      // accept rvalue ref

template<typename T>
void logAndProc(T&& param)
{
    makeLog(...);
    process(std::forward<T>(param));
}

Widget w;
logAndProc(w);             // call process(const Widget&)
logAndProc(std::move(w));  // call process(Widget&&)
```
1. 當傳入`logAndProc`的是lvalue時，`param`以`Widget&` lvalue初始化，此時`std::forward`不會轉型，因此會呼叫`process(const Widget&)`
2. 當傳入的是rvalue時，`param`用`Widget&&` rvalue初始化，`std::forward`會將之轉型成rvalue，而呼叫`process(Widget&)`
3. 由於`std::forward`多是指用在這種將參數轉發給其他function的時機，此時稱為perfect forwarding

注意若不使用`std::forward`，兩個例子都會呼叫到`process(const Widget&)`，這是因為`param`是個變數，本身是個lvalue，與他的型態無關
另外在`logAndProc`中，不能用`std::move`取代`std::forward`，因為`std::move`會無條件轉型，這樣不管傳入的是lvalue或rvalue，都會呼叫到`process(Widget&&)`


## Item 24: Distinguish universal references from rvalue references

只有在特定的狀況下，`T&&`才會被當成universal reference，其餘狀況皆為rvalue reference
1. 形式一定要是`T&&`，不能包含cv-qualifier
2. `T`一定要參與型態推導
```cpp=
// rvalue ref, No type deduction
void f(Widget&&);

// rvalue ref, same reason as above
Widget&& var1 = Widget();

// universal ref. auto needs type deduction
auto&& var2 = var1;

// rvalue ref. form is not T&&
template<typename T>
void g(const T&& param);

// rvalue ref. form is not T&&
template<typename T>
void g(std::vector<T>&& param);

// universal ref
template<typename T>
void h(T&& param);

// example of STL vector
template<class T, class Allocator = allocator<T>>
class vector {
public:
    ...
    
    // rvalue ref.
    // in push_back, T is not involved in deduction
    // (T is already decided when declaring a vector)
    void push_back(T&& x);

    // universal ref.
    // Args is independent from T
    // Args must be deduced when emplace_back is used
    template<class... Args>
    void emplace_back(Args&&... args);
};
```

universal reference幾乎可以連結到各種物件，const / non-const，violatile / non-violatile，rvalue / lvalue
運作原理在[Item 28](#Item-28-Understand-reference-collapsing)

## Item 25: Use std::move on rvalue references, std::forward on universal references

若function參數是rvalue reference，應使用`std::move`
若function參數是universal reference，應使用`std::forward`
在universal reference上用`std::move`，可能會修改到lvalue
```cpp=
struct Widget
{
    template<typename T>
    void setName(T&& newName)
    {
        // can compile, but may modify the lvalue
        // name = std::move(newName);
        
        // correct
        name = std::forward<T>(newName);
    }
    std::string name;
};

Widget w;
std::string name = "widget";
w.setName(w);  // move name into Widget, the content of name is undefined
```
這個`setName`，可以用兩個function overloading，一個接受`const std::string&`，一個`std::string&&`
```cpp=
struct Widget
{
    void setName(const std::string& newName)
    {
        name = newName;
    }
    void setName(std::string&& newName)
    {
        name = std::move(newName);
    }
    std::string name;
};
```
1. Overloading需要維護兩份code
2. 參數限定為`std::string`，若需要接收其他參數，則可能要多寫其他的function
3. 若需要更多參數，universal ref版本可以用parameter pack，使每個參數都是universal ref，overloading版本則無法
4. 可能會比較沒效率

```cpp=
Widget w;
w.setName("this is a name");
```

若為overloading的版本:
a. 用字串陣列建立`std::string`的暫存物件
b. 呼叫rvalue ref版本的`std::string::operator=`

若為universal ref版本(不會有暫存物件產生)
a. 字串陣列直接傳入`setName`中
b. 呼叫字串指標版本的`std::string::operator=`

並不是用了rvalue ref，就一定要用`std::move`
用了universal ref，也不是一定就要用`std::forward`
如下例子，僅有最後一行，是將參數轉發，其他地方因為不會修改參數，僅傳入參數名稱
```cpp=
struct Widget
{
    template<typename T>
    void setName(T&& newName)
    {
        // check if the parameter is legal first
        // function validateName will not change newName
        if (validateName(newName))
        {
            name = std::forward<T>(newName);
        }
    }
};
```

若function是by value回傳值，且回傳對象是一個rvalue ref或是universal ref的參數時，應使用`std::move`即`std::forward`
```cpp=
// rvalue ref
Matrix operator+(Matrix&& lhs, const Matrix& rhs)
{
    lhs += rhs;
    return std::move(lhs);

//    return lhs;  // copy lhs to return
}

// universal ref
template<typename T>
Fraction reduceAndCopy(T&& frac)
{
    frac.reduce();
    return std::forward<T>(frac);
}
```
1. function以by value回傳
2. 第一個參數為rvalue reference，這在operator中可能很有用，因為可以直接使用lhs來儲存運算結果
3. 若不使用`std::move`回傳，由於lhs是變數，是個lvalue，因此會copy到暫存物件再回傳
4. 因此若`Matrix`有提供move ctor，效率通常會比較好
5. 若`Matrix`沒有move ctor，`std::move`版本，依然會呼叫copy ctor，但若`Matrix`之後提供move ctor，無需改code即可提升效率
6. 同樣情形也可以應用在universal ref和`std::forward`上，回傳時用`std::forward`，才能保證在傳入rvalue時，將rvalue move到回傳值，而用lvalue時使用copy

注意若回傳的不是參數，而是區域變數，不應使用`std::move`
```cpp=
// original version
Widget makeWidget()
{
    Widget w;
    ...
    
    
    return w;  // we though w will be copied to a tmp object to return
}

// version we may improve
Widget makeWidget()
{
    Widget w;
    ...
    
    
    return std::move(w);  // OK but don't do this
}
```

這是因為compiler通常能為這種case做RVO(Return value optimization)
直接將w建構在callee的回傳值上
能夠進行RVO的條件:
1. 必須是型態與回傳值相同的local variable
2. 回傳的是local variable本身(`return w`)，即透過`std::move(w)`回傳，或是透過ref / pointer回傳，都是不行的

在C++ 17之前，RVO不是必須的
但標準有規範，若可以做RVO，但不做此最佳化的時候，必須將回傳值作為rvalue處理(即必須用`std::move`)
也就是說compiler編譯時，要不使用RVO，要不使用`std::move`
因此開發者直接寫`return w`，就可以將問題交給compiler解決
```cpp=
Widget makeWidget()
{
    Widget w;
    return w;
}

// If compiler does not apply RVO, the function must be equal as following
Widget makeWidget()
{
    Widget w;
    return std::move(w);
}
```

同樣當有function參數是by value，且型態與回傳值相同時
若是直接回傳該參數，雖然不能做RVO，但在回傳時，compiler也必須將其作為rvalue處理
```cpp=
Widget makeWidget(Widget w)
{
    ...
    return w;  // the same as 'return std::move(w)'
}
```

結論：
1. 當需要做轉發時，應使用`std::move`和`std::forward`將參數轉發
2. 要將rvalue ref或universal ref作為by value的回傳值時，也應使用`std::move`和`std::forward`
3. 但如果是要將local variable以by value形式回傳時 (return type與該變數型態相同)，不應使用`std::move`

## Item 26: Avoid overloading on universal references

```cpp=
std::multiset<std::string> names;

void logAndAdd(const std::string& name)
{
    log("logAndAdd");
    names.emplace(name);  // add name.
                          // emplace uses universal ref
}

std::string s1("s1");
logAndAdd(s1);                 // pass lvalue
logAndAdd(std::string("s2"));  // pass rvalue
logAndAdd("s3");               // pass string literal
```

1. `s1`是lvalue，在pass進`logAndAdd`時，`name`是連結到lvalue，在存入`names`時，無法避免copy
2. `s2`是個rvalue，在傳入`logAndAdd`時，`name`是連結到一個rvalue，由於`name`本身是lvalue，因此存入`names`時，依然要copy。但其實因為連結到rvalue，其實是可以move
3. `s3`是string literal，傳入`logAndAdd`時，會先建立暫存物件，`name`一樣是lvalue，因此會copy進`names`，此狀況其實應該可以直接把`s3`pass給`names`，連暫存物件都不需要建立

分析上述三點，應該用universal ref來改善
```cpp=
std::multiset<std::string> names;

template<typename T>
void logAndAdd(T&& name)
{
    log("logAndAdd");
    names.emplace(std::forward<T>(name));
}
```
1. pass`s1`時，和原本狀況一樣，`name`會連結到lvalue，因此`names`會收到lvalue
2. pass`s2`時，`name`連結到rvalue ref，`names.emplace`可以做move
3. pass`s3`時，`name`會是string literal，`names`可以直接從string literal建立物件

有了univeral ref後，若還有其他overloading function，可能會有問題
```cpp=
void logAndAdd(int index)
{
    ...
}

std::string s1("s1");
logAndAdd(s1);                 // call universal ref version
logAndAdd(std::string("s2"));  // call universal ref version
logAndAdd("s3");               // call universal ref version
logAndAdd(0);                  // call int version

short index = 0;
logAndAdd(index);              // call universal ref version
```
1. 若是用`int`呼叫`logAndAdd`，會呼叫到`int`版本，這是因為`logAndAdd(int)`是exactly match
2. 若用`short`呼叫，有兩個版本選擇，一個是template產生的short版本，一個是`int`版本，由於template是exactly match，而`int`版本要經過promotion才能符合，因此會選擇universal ref

若universal ref function是constructor，可能會更糟
```cpp=
class Person
{
public:
    template<typename T>
    Person(T&& n) : name(std::forward<T>(n)) { ... }
    
    // may be customized or compiler generated
    Person(const Person& p) { ... }

    // may be customized or compiler generated
    Person(Person&& p) { ... }
private:
    std::string name;
};

Person p1("a person");
auto p2(p1);  // call universal ref version

const Person p3("another persion");
auto p4(p3);  // call copy ctor
```
1. `Person`定義一個universal ref版本的ctor，copy和move ctor可能是使用自定義，或是compiler自動產生的
2. 當用`p1`去建立`Person`物件`p2`時，有兩個選擇
一個是universal ref產生的，另一個是copy ctor
由於copy ctor需要的是`const Person&`，`p1`需要增加`const`qualifier才能match，因此會呼叫universal ref
3. 用`p3`建立`Person`物件`p4`時，也有兩種選擇，universal ref和copy ctor同樣好的狀況下，compiler會挑選non-template版本，因此呼叫copy ctor

若有繼承時，可能會更加困惑
```cpp=
class SpecialPerson : public Person
{
public:
    SpecialPerson(const SpecialPerson& rhs)
        : Person(rhs)  // will call Person(T&&) version
    {
    }

    SpecialPerson(SpecialPerson&& rhs)
        : Person(std::move(rhs))  // will call Person(T&&) version
    {
    }
};
```
在copy / move ctor中，pass給`Person`class的型態實際上是`SpecialPerson`，因此universal ref版本會將型態推導為`SpecialPerson`，優於`Person`的copy / move ctor

## Item 27: Familiarize yourself with alternatives to overloading on universal references

### 避免overloading
利用function名稱將universal ref版本和其他版本分開
不適用於constructor

### 用const T& 代替 universal ref
即以前C++ 98的方式，優點是簡單易懂，缺點是效能可能較低

### 用 T (傳值) 取代 universal ref
若確定function參數就是要copy / move 給其他參數 (如constructor)，用by value的方式可能可以同時兼顧效能與簡單的設計
[Item41](#Item-41-Consider-pass-by-value-for-copyable-parameters-that-are-cheap-to-move-and-always-copied)專門描述這樣的狀況

```cpp=
class Person {
public:
    Person(std::string n)
        : name(std::move(n)) { ... }

    Person(int index) { ... }
}
```

### 用tag dispatch
雖然絕大多數的參數都能用universal ref去做match，但如果function參數中有non universal ref，只要該參數不符合，就能夠呼叫其他的function，這樣的non universal ref參數稱為tag，能夠引導compiler呼叫不同的overloading function
```cpp=
template<typename T>
void logAndAdd(T&& name)
{
    logAndAddImpl(
        std::forward<T>(name),
//        std::is_integral<T>()  // wrong. T may be a lvalue ref
        std::is_integral<typename std::remove_reference<T>::type>()
    );
}

// universal ref version
template<typename T>
void logAndAddImpl(T&& name, std::false_type)
{
    names.emplace(std::forward<T>(name));
}

// int version
void logAndAddImpl(int index, std::true_type)
{
    ...
}
```
1. 兩個`logAndAddImpl`function即是[Item26](#Item-26-Avoid-overloading-on-universal-references)中本來的`logAndAdd`實作
2. `logAndAddImpl`的最後一個參數即為標籤，型態分別是`std::false_type`和`std::true_type`，不論第一個參數是什麼，如果最後一個參數型態是`std::true_type`，就呼叫`int`版本的`logAndAddImpl`，反之呼叫universal ref版本
3. 新的`logAndAdd`只接受universal ref，是給user的interface
4. 在呼叫`logAndAddImpl`時，最後一個參數用`std::is_integral`去判斷`T`是否是整數型態，如果是整數，`std::is_integral<T>`會是`std::true_type`，藉此引導compiler呼叫`int`版本
5. `std::is_intergral`中應使用`std::remove_reference`，確定傳給`is_integral`的不是一個reference
NOTE: 當傳lvalue給`logAndAdd`時 (e.g. 傳入變數`int i`)，`T&&`和`T`都推導為`int&`
T為reference to int而不是int，因此`std::is_integral<T>`為false
當傳入rvalue時(e.g. `std::move(i)`)，`T&&`為`int&&`，`T`為`int`
為確保兩種都可以work，才要用`remove_reference`
6. `std::true_type`/`false_type`時常用來做型態類型的`true`/`false`，在template code中時常被使用

此方式也不適用於constructor，因為tag dispatch的基礎是interface只有一個，且用universal ref表示
compiler可能會自行產生copy / move constructor，打破這個規則

### 限制接受universal ref參數的template
可以用`std::enable_if`來限制universal ref只能接受特定種類的參數
`std::enable_if`利用[Substitution Failure Is Not An Error](#SFINAE) (SFINAE)，讓template變成無法被產生，compiler就無法挑選該template
概念上，預設template都是enable狀態，都是compiler選擇的對象。`std::enable_if`使得template僅有在條件符合的狀況下才是enable，不符合就是disable

```cpp=
class Person
{
public:
    template<typename T,
             typename = typename std::enable_if<condition>::type>
    Person(T&& n);
};
```
1. constructor的第二個template參數即為enable的條件，若條件不符
2. 第二個參數的`typename =`沒有名稱，是因為這參數只代表條件，實際上程式碼不會去使用
3. 第二參數的`typename std::enable_if`中，`typename`是必須的，因為`enable_if`中會用到`T`，此`typename`標記了`T`是一個template parameter
4. `condition`為條件
5. `std::enable_if<condition>::type`的`type`實際是什麼並不重要。重要的是如果條件符合，`type`才會產生，如果條件不符，`std::enable_if`不會有`type`，而依據SFINAE，此template推導會失敗(因為`enable_if`沒有`type`)，導致此template function不會出現

利用`enable_if`，我們可以讓universal ref的constructor只在以下條件時產生
1. 當`T`不是Person時
2. 當`T`不是Person的衍生type時
3. 當`T`不是整數型態時

```cpp=
class Person
{
public:
    template<
        typename T,
        typename = typename std::enable_if<
            !std::is_base_of<Person, typename std::decay<T>::type>::value
            &&
            !std::is_integral<std::remove_reference_t<T>>::value
        >::type
    explicit Person(T&& n);
    explicit Person(int index) { ... }
};
```
1. `enable_if`中包含兩個條件，用`&&`連接
2. 第一條件表示`T`的base type不可以是`Person`，這也包含`Person`本身
3. `std::decay`是讓`T`退化，此包含移除reference和cv-qualifier，若`T`是function或陣列型態，也會退化成pointer，這功能確保`T`僅代表型態 (若包含reference等會導致`is_base_of`的結果不如我們預期)
4. `is_integral`的條件和上一段的[tag dispatch](#用tag-dispatch)相同
5. 在C++ 14中可用`enable_if_t`取代`enable_if<>::type`，減少程式碼，`std::decay_t`也是一樣的方式，此時`typename`就可以省略了

```cpp=
class Person
{
public:
    template<
        typename T,
        typename = std::enable_if_t<
            !std::is_base_of<Person, std::decay_t<T>>::value
            &&
            !std::is_integral<std::remove_reference_t<T>>::value
        >
    explicit Person(T&& n);
    explicit Person(int index) { ... }
};
```

結論：
通常用universal ref來做perfect forward可以增進效率，其允許string literal直接傳入function之中，減少暫存物件產生的成本
但如果傳入不合法的參數時，compiler所產生的error message可能會很多，也很難理解
如
```cpp=
Person p(u"a char16_t string");  // use const char16_t[] to construct
```
`std::string`和`int`無法用`char16_t`的array/pointer產生，因此這是不合法的參數
如果不用universal ref，error message會直接在這行指出`const char_16_t[]`是不合法參數
但用universal ref，參數會傳入constructor，直到perfect forward給`Person`的data member時，才會發生錯誤，error message會顯示在constructor中，訊息也很長，比較不直覺
此問題在多次perfect forward時候可能會更糟糕

對`Person`的例子，因為我們清楚知道universal ref的參數是用來初始化`std::string`，可以透過在constructor中加入`static_assert`來提供更好的error message

```cpp=
class Person
{
    template<typename T, typename = std::enable_if_t<...>
    Person(T&& n)
    {
        static_assert(
            std::is_constructible<std::string, T>::value,
            "Parameter n can't be used to construct a std::string"
        );
        ...
    }
};
```

### SFINAE
TODO

## Item 28: Understand reference collapsing

universal ref會根據傳入的參數不同，導致`T`的不同，有可能將`T`推導為lvalue ref，或是一個rvalue
```cpp=
template<typename T>
void func(T&& param);

Widget widgetFactory();  // return rvalue
func(widgetFactory());   // T is deduced to Widget

Widget w;
func(w);                 // T is deduced to Widget&
```

請注意C++不允許宣告reference to reference，如下code是不合法的
```cpp=
int a = 0;
int& r = a;     // ok
int& & rr = r;  // invalid
```
但當把lvalue傳入universal ref時，`T`被推導為lvalue ref，再將之與`&&`結合會產生`T& &&`
compiler不會把這樣的狀況作為error，是因為有reference collapse的規則，把這種ref to ref的狀況變成合法的reference
reference collapse只會發生在4種狀況
1. template實體化時
2. `auto`的推導
3. 建立與使用`typedef`和alias declaration
4. `decltype`

由於reference有分兩種：lvalue ref和rvalue ref
因此這種reference to reference的形式有四種
1. `T& &`
2. `T& &&`
3. `T&& &`
4. `T&& &&`

reference collapse的規則是：只要其中一個reference是lvalue ref，結果就會是lvalue ref，僅有當兩者皆為rvalue refer時，結果才會是rvalue ref
1. `T& &` -> collapse to `T&`
2. `T& &&` -> collapse to `T&`
3. `T&& &` -> collapse to `T&`
4. `T&& &&` -> collapse to `T&&`
```cpp=
template<typename T>
void func(T&& param);

Widget w;
func(w);

// apply Widget to func
void func(Widget& && param);

// reference collapse will apply to the above line
// and will become
void func(Widget& param);
```

這機制是`std::forward`運作的核心，如以下的範例
```cpp=
// example of std::forward implementation
template<typename T>
T&& forward(typename remove_reference<T>::type& param)
{
    return static_cast<T&&>(param);
}

template<typename T>
void f(T&& fParam)
{
    someFunc(std::forward<T>(fParam));
}
```
當`f()`傳入Widget的lvalue時，`T`被推導為`Widget&`，以下將型態帶入`f()`和`std::forward`的結果
```cpp=
// Apply Widget& to T
Widget& && forward(typename remove_reference<Widget&>::type& param)
{
    return static_cast<Widget& &&>(param);
}

void f(Widget& fParam)
{
    someFunc(std::forward<Widget&>(fParam));
}
```
將reference collapse apply到`std::forward`就會如下
```cpp=
// Apply Widget& to T
Widget& forward(Widget& param)
{
    return static_cast<Widget&>(param);
}
```
因此得到結果，會將`param`轉型為`Widget&`，並且回傳`Widget&` (即不做事情)，這符合`std::forward`對lvalue參數的行為

當`f()`傳入Widget的rvalue時，`T`推導為`Widget`，以下是帶入`Widget`的結果
```cpp=
// Apply Widget to T
Widget&& forward(typename remove_reference<Widget>::type& param)
{
    return static_cast<Widget&&>(param);
}

void f(Widget&& fParam)
{
    someFunc(std::forward<Widget&&>(fParam));
}
```
這裡因為沒有reference to reference的情況，不會發生reference collapse
而`std::forward`會把`param`轉型為`Widget&&`並回傳`Widget&&`

`typedef`、alias declaration與`decltype`也會apply reference collapse
```cpp=
int i = 0;
int& r = i;
decltype(r)&& rvalue_ref = r;  // type of rvalue_ref is int&

using RvalueRef = int&&;
RvalueRef& rvalue_ref2 = i;  // type of rvalue_ref2 is int&
```

## Item 29: Assume that move operations are not present, not cheap, and not used

此條款描述move operation在某些狀況下可能不如預期
1. Move operation根本不存在
從[Item 17](#Item-17-Understand-special-member-function-generation)可知道，當有customized dtor、copy ctor、copy assignment或是任一一個move operation時，剩下的move operation不會被產生
因此許多狀況下，除非開發者有特別定義move操作，否則class可能並不支援move語意
2. Move operation成本不見得比較低
即使有明確提供Move operation，這成本也不一定比copy低
Move操作成本比較低，通常都是因為記憶體被配置在heap上(如`std::vector`)，做Move時是將指標做轉移，而不是像copy一樣配置一塊新的記憶體
![](https://i.imgur.com/Hzc43hL.png)
但若記憶體都是在stack上(如`std::array`)，做move時，可以對每個data member都使用move操作，但假設data member不支援，那實際上成本也是和copy一樣
如下圖，當`aw1`要被move到`aw2`時，`std::array`會對每個element做move操作，如果`Widget`有支援move，那整體速度就可能會比copy快
但若`Widget`不支援move操作，則實際上還是會copy
![](https://i.imgur.com/q6VXJMf.png)
3. Move operation不見得會被使用
這類型和實作有關
a. 如`std::string`可能會使用small string optimization (SSO)，當字串長度小於一定值(如15個char)時，`std::string`會使用物件內部的buffer來存，而不在heap上配置空間
對這種`std::string`做move操作，實際上也會copy。當然通常這樣的成本都不會太高
b. 另一種可能是class並不符合呼叫move operation的條件，如[Item 14](#Item-14-Declare-functions-noexcept-if-they-won’t-emit-exceptions)提到，STL中某些容器操作有提供例外安全的保證，對此種容器而言只有當element有提供`noexcept`的move operation，才會使用，否則會copy
4. Move的來源是lvalue
通常狀況下對lvalue做move operation，實際上還是會copy
只有在如[Item 25](#Item-25-Use-std::move-on-rvalue-references-std::forward-on-universal-references)最後所提到的，一個function以by value方式回傳一個local variable時，才會對lvalue做move operation而不是copy

在一些無法確定type的情況(如template)，應要有心理準備，假設處理的class中，move可能不存在、成本不低、且可能不被使用。這狀況下就不應依賴move operation所帶來的好處，而要保守的處理class

## Item 30: Familiarize yourself with perfect forwarding failure cases

forward: 轉發表示一個function將參數forward給另一個function，令接收的function可以接收到相同的參數，通常討論轉發時，只討論reference (因為by value時候僅是copy)
perfect forward: 也是轉發參數，但不僅是轉發物件，同時也將參數的type也轉發給下一個function，這包含該reference是lvalue或是rvalue，以及是否有cv-qualifier

以下是通常用到perfect forward的function會有的樣子，參數是variadic的universal ref
body則是用`std::forward`把`params`傳給其他function
```cpp=
template<typename... Ts>
void fwd(Ts&&... params)
{
    f(std::forward<Ts>(params)...);
}
```

perfect forward失敗的意思是，將參數直接傳遞給`f`時的狀況，和將參數傳給`fwd`，再傳給`f`時的狀況不同
通常是兩種會導致失敗
1. compiler無法推導出`fwd`中某些參數的type，以至於無法編譯
2. compiler從`fwd`推導出錯誤的type，以至於將錯誤的type傳遞給`f`

以下是perfect foward失敗的情境

### 大括號初始子
由於template parameter不能接受大括號初始子(除了`param`明確寫為`std::initializer_list<T>`)
因此以下狀況下，直接呼叫`f`可以用大括號，但透過`fwd`就不行
```cpp=
void f(const std::vector<int>& v);

f({1, 2, 3});    // ok
fwd({1, 2, 3});  // error. Fail to deduce type T

// alternative way
auto v = { 1, 2, 3};  // ok. v is std::initializer_list
fwd(v);               // ok. perfect forward to f
```

### 用`0`或`NULL`作為null pointer
用`0`或`NULL`傳給`fwd`時，type會被推導為`int`而不是指標
此狀況用`nullptr`即可

### 只有宣告的的`static const`整數data member
通常，對於`static const`的data member，如果只是要取值，是只需要宣告不需要定義
這是因為compiler可以對這些數值做const propagation的最佳化
```cpp=
class Widget
{
public:
    static const int Value = 28;
};
// const int Widget::Value = 28;  // No definition here
std::vector<int> v;
v.reserve(Widget::Value);  // OK. does not use the Widget::Value's address

void g(const int&);
// g(Widget::Value);      // error. Invoke g will use Widget::Value's address
```

同理，如果將`Widget::Value`傳入`fwd`，也會導致編譯(linkage)錯誤，因為universal ref是一個reference，會對`Widget::Value`取值
```cpp=
f(Widget::Value);    // OK.
fwd(Widget::Value);  // linkage error. Use Widget::Value's address
```

### 傳遞一個有overloading的function或傳遞template function

假設function `f`是接受一個function pointer (or function type)如下
```cpp=
void f(int (*pf)(int));

// the following declaration is the same as above
void f(int pf(int));

// processVal is overloading
int processVal(int);
int processVal(int, int)

f(processVal);     // ok. pass processVal(int)

// fwd(processVal);  // error. Don't know which processVal should use
```
將`processVal`傳給`f`可以work，是因為compiler知道`f`需要的是哪種type
但在`fwd`時，光從universal ref上，無法判斷應該要哪個`processVal`，因此有error

同樣的問題在template function也是一樣，因為template function本身代表functions的集合，而不是單一一個function
```cpp=
tempalte<typename T>
T workOnVal(T param);

// fwd(workOnVal);  // error.
```

要解決此問題，就要在傳給`fwd`時，明確認compiler知道傳遞的型態是什麼
```cpp=
using ProcessFunc = int(*)(int);
ProcessFunc proc = processVal;   // ok. processVal(int) is chosen
fwd(proc);  // ok.

fwd(static_cast<ProcessFunc>(processVal)); // OK
fwd(static_cast<ProcessFunc>(worokOnVal)); // OK
```

### 傳遞bitfield

C++標準規定，universal ref不能接受non-const的bitfield
```cpp=
struct A
{
    std::uint32_t field1:16,
                  field2:16;
};

void f(std::size_t sz);

A a;
f(a.field2);  // ok

fwd(a.field2);  // error. non-const ref cannot bind to bitfield
```

由於任何傳遞bitfield的狀況，compiler都會將之變成傳遞copy of the bitfield value
這是因為bitfield沒有辦法取address，無法被reference
而當bitfield是`const`時可以被傳遞，則是因為`const`時不能修改bitfield，compiler可以安心的把bitfield的值取出來，copy到function的參數上
```cpp=
const A a;
f(a.field2);
fwd(a.field2);  // ok a.field2 is const
```

因此要解決此問題，只要先將bitfield的值copy到一個變數，再傳遞該變數就可以了
```cpp=
auto field = static_cast<std::uint16_t>(a.field2);
fwd(field);
```

# Chapter 6. Lambda Expressions

Lambda是C++ 11後引入的語法糖，並在C++ 14得到擴充
語法為
```cpp=
[ capture list ] (parameters ) -> trailing-return-type
{
    method definition
}
```
若`trailing-return-type`可以被compiler推導，也可以不寫

當寫一個lambda時，compiler實作會給他一個獨一無二的type
如`auto f = []() {};`，`f`是一個object，有ctor和dtor，並且有`operator()()`
```cpp=
auto f = []() { std::cout << "f()" << std::endl; };
// the same as
struct anonymous
{
    inline auto operator()() const
    {
        std::cout << "f()" << std::endl;
    }
};

int i;
auto f2 = [&i]() { std::cout << i << std::endl; };
// the same as
struct anonymous2
{
    int& m_i;
    anonymous2(int& i): m_i(i) {}
    inline auto operator()() const
    {
        std::cout << i << std::endl;
    }
};

auto f3 = [i]() { std::cout << i << std::endl; };
// the same as
struct anonymous3
{
    int m_i;
    anonymous3(int i): m_i(i) {}
    inline auto operator()() const
    {
        std::cout << i << std::endl;
    }
};
```
1. lambda expression是一個expression，他是程式碼的一部份，如`auto f = []() {...}`中，`[]() {...}`是lambda expression
2. closure是lambda在runtime時建立的object，依據capture mode不同，closure擁有資料的copy或是reference，如`auto f = []() {...}`中，`f`是runtime所建立的closure
3. closure的本體是一個callable的物件，如上例中的`anonymous`、`anonymous2`和`anonymous3`所產生的物件
4. closure class是實體化closure的類別，如例子中的`struct annonymous`
5. capture方式有by value，by ref和no capture，以下是capture方式

| 寫法          | Capture方式                                       |
| ------------ | ------------------------------------------------- |
| `[](){}`     | no capture                                        |
| `[=](){}`    | captures everything by copy                       |
| `[&](){}`    | captures everything by reference                  |
| `[x](){}`    | captures x by copy                                |
| `[&x](){}`   | captures x by reference                           |
| `[&,x](){}`  | captures x by copy, everything else by reference  |
| `[=,&x](){}` | captures x by reference, everything else by value |

5. 小括號表示要傳入的參數
6. 如果沒有capture(如`anonymous`)，class裡就沒有data member，但closure還是一個object，能否轉型成function pointer要看compiler實作而定
7. 如果有capture(如`anonymous2`和`anonymous3`)，則有data member，當by value capture時，就會copy進data member

若要把lambda作為參數傳入function時，可以利用template完成
```cpp=
template<typename Func>
void f(Func func)
{
    func();
}
auto lambda_func = []() { ... };
f(lambda_func);
```

當capture mode有by value時，`operator()`by default都是const function，如果要讓其不是const，可以加入`mutable`specifier
```cpp=
int i = 0;
int j = 0;
auto f = [i]() mutable { i++; };     // mutable must exist
auto f2 = [i, &j] mutable { i++; };  // mutable must exist
```

若在member function中，想要capture member data的話，需要capture `this`指標，使用`[this]`、`[=]`或`[&]`都可以
需要注意capture的是`this`而不是member data，因此member data是可以被修改的
```cpp=
class A
{
public:
    int m_i;
    void f()
    {
        // the followings are the same
        [=]() { std::cout << m_i << std::endl; m_i++; }();
        [this]() { std::cout << m_i << std::endl; m_i++; }();
        [&]() { std::cout << m_i << std::endl; m_i++; }();
    }
};
```

可以用lambda建立high-order的function
```cpp=
auto less_than = [](int x)
{
    return [x](int y)
    {
        return y < x;
    };
};
auto less_than_5 = less_than(5);
std::cout << less_than_5(3) << std::endl;   // print 1
std::cout << less_than_5(10) << std::endl;  // print 0
```

### C++ 14以後加入的功能：
可以在capture mode中定義變數，如下例中
`tmp`是by value的capture `i`，`tmp2`是by ref的capture `i`，`tmp3`是by value的被初始化為`0`
```cpp=
int i = 10;
auto f = [tmp = i, &tmp2 = i, tmp3 = 0]()
    {
        std::cout << tmp << "," << tmp2 << "," << tmp3 << std::endl;
    };
```

Lambda的參數可以用`auto`，讓compiler做型態推導
```cpp=
auto f = [](auto a, auto b) {};
// the same as
struct anonymous
{
    tempalte<typename T0, typename T1>
    auto operator()(T0 a, T1 b) const
    {
    }
};
```

Lambda也可以接受variadic generic參數，等同於variadic template
```cpp=
auto f = [](auto... param) {};
// the same as
struct anonymous
{
    template<typename T>
    auto operator()(T... param)
    {
    }
};
```

接受generic type參數的lambda適用template型態推導，因此也可以用cv-qualifer、universal ref
而由於template不接受大括號初始化，因此generic type lambda也無法接受大括號初始子
```cpp=
auto f = [](auto&& a) {};  // ok. universal ref

auto g = [](auto a) {};
// g({1, 2, 3});  // error. cannot infer type for {1, 2, 3}
```

## Item 31: Avoid default capture modes

當closure的生命週期大於capture的參數時，by reference capture mode會導致dangling reference
```cpp=
using FilterContainer = std::vector<std::function<bool(int)>;
FilterContainer filters;

void addDivisorFilter()
{
    int cal1 = computeValue1();
    int cal2 = computeValue2();
    
    int divisor = computeDivisor(cal1, cal2);
    
    // dangling ref to divisor
    filters.emplace_back(
        [&](int value) { return value % divisor == 0; }
    );
    
    // explicitly specify referencing to divisor
    // still dangling ref to divisor
    filters.emplace_back(
        [&divisor](int value) { return value % divisor == 0; }
    );
}
```
1. 在`addDivisorFilter`中，lambda capture `divisor` by reference，並且被塞入全域變數中
2. 但`divisor`在離開`addDivisorFilter`後就被銷毀，導致`filters`之後使用closure時，會ref到不存在的東西
3. 明確的寫`[&divisor]`可以提醒開發者，lambda依賴於`divisor`，`divisor`的生命週期應該至少要和該closure一樣長
4. 如果確定closure會立刻被使用 (如STL演算法)且不會被複製，by ref是可以的
```cpp=
template<typename T>
void work(const std::vector<T>& container)
{
    auto cal1 = computeValue1();
    auto cal2 = computeValue2();
    auto divisor = computeDivisor(cal1, cal2);

    if (std::all_of(begin(container), end(container),
        [&](const T& value)
        {
            return value % divisor == 0;
        }))
    {
        ...
    }
}
```

`addDivisorFilter`的問題，可以用capture by value方式解決
但某些狀況，如果capture的是`this` pointer，依然會有dangling pointer的問題
```cpp=
class Widget
{
public:
    void addFilter() const;
private:
    int divisor;
};
void Widget::addFilter() const
{
    // capture this pointer by value
    filters.emplace_back(
        [=](int value) { return value % divisor == 0;}
    );
}

void func()
{
    auto p = std::make_unique<Widget>();
    p->addFilter();
}
// p is deleted when leaving the scope
// filters holdes a closure with dangling pointer to Widget
```
1. lambda的capture只能對區域變數、參數、以及`this`指標做擷取，因此如下的寫法是錯的
```cpp=
void Widget::addFilter() const
{
    // error. divisor not available
    filters.emplace_back(
        [](int value) { return value % divisor == 0; }
    );
    
    // error. no local divisor to capture
    filter.emplace_back(
        [divisor](int value) { return value % divisor == 0; }
    );
}
```
2. 在member function中，使用by value的預設capture mode時，`this`會以by value方式被擷取
```cpp=
void Widget::addFilter() const
{
    filter.emplace_back(
        [=](int value) { ... }
    );

    // the same as following
    auto currentObjPtr = this;
    filter.emplace_back(
        [currentObjPtr](int value) { ... }
    );
}
```
3. 在by value狀況下想解決生命週期問題，可以明確地將member data `divisor`先copy至區域變數，在做capture
```cpp=
void Widget::addFilter() const
{
    // C++ 11 version
    auto divisorCopy = divisor;
    filter.emplace_back(
        [divisorCopy](int value) { value % divisorCopy == 0; }
    );
}
```
4. C++ 14更可以用init capture (generalized lambda capture)方式解決
```cpp=
void Widget::addFilter() const
{
    // C++ 14 version
    filters.emplace_back(
        [divisor = divisor](int value) { value % divisor == 0; }
    );
}
```

預設的by value capture mode還有一個缺點，會讓開發者以為該lambda可以自給自足，不與其他物件有關聯
但當lambda碰到`static`變數時，lambda無法擷取`static`變數，但卻能使用，而使用預設的by value capture mode會讓人以為`static`變數的值被copy進closure裡了，實際上不然
```cpp=
void addDivisorFilter()
{
    static auto cal1 = computeValue1();
    static auto cal2 = computeValue2();
    static auto divisor = computeDivisor(cal1, cal2);

    // does not capture anything
    filters.emplace_back(
        [=](int value) { return value % divisor == 0; }
    );

    ++divisor;  // modify divisor value
}
```
上例中，lambda沒有capture任何東西，該lambda直接使用`static`變數`divisor`，當`divisor`值改變時，lambda執行的內容就會改變
但若閱讀程式碼時粗心，看到`[=]`就誤以為lambda已將所有需要的物件都copy至closure了，可以自給自足，造成誤會

## Item 32: Use init capture to move objects into closures

lambda無法使用move capture，在C++ 11和C++ 14分別有不同的解法
C++ 14可以用init capture做到
init capture必須有
1. lambda產生的closure class中，所使用的data member名稱
2. 用來初始化該data member的expression
3. init capture不支援default capture mode
```cpp=
class Widget
{
public:
    bool isValidated() const;
};

auto p = std::make_unique<Widget>();
auto func = [p = std::move(p)]
    {
        return p->isValidated();
    };

// the same as above
auto func2 = [p = std::make_unique<Widget>()]
    {
        return p->isValidated();
    };
```
1. `std::unique_ptr`只能move，不能copy
2. 在`[p = std::move(p)]`中，左邊的`p`代表closure class中data member名稱，scope是在closure class中，和`func`所在的scope不同
3. 右邊的`std::move(p)`代表用來初始化data member的expression，scope和`func`相同，且和closure class的scope不同
4. 初始化列表中所代表的意思是：在closure中建立data member `p`，並且將`std::move`套用在區域變數`p`上，其結果用來初始化data member `p`
5. lambda的body的scope和closure class相同，因此裡面使用的`p->isValidated()`是使用closure class中的data member `p`
6. `func2`的`p`是直接用expression `std::make_unique`初始化

由於lambda只是將建立callable的class做簡化，在C++ 11中，可以透過自定義class達到相同效果
```cpp=
class IsValidated
{
public:
    using DataType = std::unique_ptr<Widget>;
    
    explicit IsValidated(DataType&& p)
        : p(std::move(p))
    {
    }
    
    bool operator()() const
    {
        return p->isValidated();
    }

private:
    DataType p;
};

auto func = IsValidated(std::make_unique<Widget>());
```

C++ 11中也可以用lambda做模擬
1. 將要capture的物件用`std:move`搬移到`std::bind`的function object中
2. 在lambda的參數中，使用reference，ref到captured的物件
```cpp=
auto p = std::make_unique<Widget>();
auto func = std::bind(
    [](const std::unique_ptr<Widget>& p) { p->isValidated(); },
    std::move(p)
);
```
1. `std::bind`與lambda一樣，會回傳一個callable的object，以下代稱bind object
2. `std::bind`的第一個參數是一個callable object，第二參數表示要傳給callable object的參數
3. bind object將所有傳給`std::bind`的參數都保存下來，成為data member，若參數用lvalue方式傳給`std::bind`，用copy，若用rvalue傳遞，用move
4. 呼叫bind object時，bind object會將data member當作參數，callable object (當初`std::bind`的第一個參數)
5. 此版本與C++ 14的不同是，再呼叫`func`時，使用的`p`是bind object內部的data member的lvalue reference，而不是rvalue reference

由於lambda預設產生的function是`const`，`std::bind`中，要用`const T&`來宣告參數
如果要能夠修改參數的值，要使用`mutable`標記lambda
```cpp=
auto func = [p = std::make_unique<Widget>()] mutable
    {
        ...  // p is mutable
    };
auto func2 = std::bind(
    [](std::unique_ptr<Widget>& p) mutable
    {
        // p is mutable
    },
    std::make_unique<Widget>()
);
```

## Item 33: Use decltype on auto&& parameters to std::forward them

C++ 14導入generic labmda，讓參數可以透過template推導型態
```cpp=
auto f = [](auto x) { return func(x); };
// the same as
struct anonymous
{
    template<typename T>
    auto operator()(T x) const
    {
        return func(x);
    }
};
```
此lambda僅是將`x` forward給`func`，如果`func`對lvalue和rvalue處理方式不同，則應使用universal ref，並用`std::forward`
```cpp=
auto f = [](auto&& x) { return func(std::forward<decltype(x)>(x)); };
```
1. `std::forward`需要template parameter，但在lambda中，沒有明寫template parameter，因此需要用其他方式來決定
2. `decltype`可用來取得`x`的型態，因此`decltype(x)`可以傳給`std::forward`來做perfect forward

NOTE: 在一般template function用`std::forward`時，通常是以下狀況
```cpp=
template<typename T>
void fwd(T&& param)
{
    func(std::forward<T>(param));
}

auto f = [](auto&& x) { return func(std::forward<decltype(x)>(x)); };

int x;
fwd(x);
f(x);

fwd(0);
f(0);
```
1. 當傳入lvalue (`x`)時，`T`是lvalue reference，給`std::forward`的`T`是`int&`，在lambda中傳入lvalue時，`decltype(x)`也是lvalue ref，兩者狀況一致
```cpp=
// Apply int& to template parameter T (when calling fwd(x))
void fwd(int& && param)
{
    func(std::forward<int& &&>(param));
}
// after ref collapse
void fwd(int& param)
{
    func(std::forward<int&>(param));
}

// Apply int& to auto (when calling f(x))
auto f = [](int& && x) { return func(std::forward<decltype(x)>(x)); }; 
// after ref collapse
auto f = [](int& x) { return func(std::forward<int&>(x)); };
```
2. 當傳入rvalue (`0`)時，`T`是rvalue (沒有reference)，給`std::forward`的`T`是`int`，這和lambda傳入rvalue時的狀況不同，`decltype(x)`將會是rvalue ref
```cpp=
// Apply int to template parameter T (when calling fwd(0))
void fwd(int && param)
{
    func(std::forward<int &&>(param));
}
// after ref collapse
void fwd(int&& param)
{
    func(std::forward<int>(param));
}

// Apply int& to auto (when calling f(0))
auto f = [](int && x) { return func(std::forward<decltype(x)>(x)); }; 
// after ref collapse
auto f = [](int&& x) { return func(std::forward<int&&>(x)); };
```
3. 雖然在傳入rvalue時兩者狀況不同，但對`std::forward`而言，`T`是`int`還是`int&&`並無差別，因此在lambda中使用`std::forward<decltype(x)>`是可以的
```cpp=
// std::forward implementation
template<typename T>
T&& forward(remove_reference<T>& param)
{
    return static_cast<T&&>(param);
}

// Apply int to T (no need ref collapse)
int&& forward(int& param)
{
    return static_cast<int&&>(param);
}

// Apply int&& to T
int&& && forward(int& param)
{
    return static_cast<int&& &&>(param);
}

// After ref collapse
// The same as passing int to T
int&& forward(int& param)
{
    return static_cast<int&&>(param);
}
```

## Item 34: Prefer lambdas to std::bind

lambda相較於`std::bind`有許多好處，更有可讀性，表達性更高，且更有效率
考慮以下例子，有一function `setAlarm`，會在某個時間點`t`，會觸發鬧鈴長達`d`的時間，音樂可以被設定
```cpp=
using Time = std::chrono::steady_clock::time_point;
enum class Sound { Beep, Siren, Whistle };
using Duration = std::chrono::steady_clock::duration;

// alarm at time t, make sound s for duration d
void setAlarm(Time t, Sound s, Duration d);
```
接著，我們希望有個function，是可以設定alarm在1小時後觸發，duration 30秒，但是聲音是什麼，由參數決定
在lambda中可以用以下寫法，當呼叫`setSoundL`時，會設定一個鬧鈴，在1小時後觸發，達30秒，聲音是使用參數`s`
```cpp=
// C++ 11 version
auto setSoundL =
    [](Sound s)
    {
        using namespace std::chrono;
        // alarm to go off in an hour for 30 seconds
        setAlarm(steady_clock::now() + hours(1), s, seconds(30));
    };

// C++ 14 version
auto setSoundL =
    [](Sound s)
    {
        using namespace std::chrono;
        using namespace std::literals;  // for C++ 14 suffixes
        // alarm to go off in an hour for 30 seconds
        setAlarm(steady_clock::now() + 1h, s, 30s);
    };
```

如果用`std::bind`，可能會用以下做法
```cpp=
using namespace std::chrono;
using namespace std::literals;
using namespace std::placeholders;

auto setSoundB = std::bind(
    setAlarm,
    steady_clock::now() + 1h,  // incorrect
    _1,
    30s
);
```
1. `placeholders::_1`表示`setAlarm`中對應的的參數是未知，將會由`setSoundB`的第一個參數做取代，由於placeholder不帶參數，呼叫`setSoundB`時若不確定型態，就必須再確認`setAlarm`中對應的參數型態是什麼，與lambda相比表達性較低
2. 由於`steady_clock::now() + 1h`是作為`std::bind`的參數，會在`std::bind`呼叫時被evaluate，因此timer會在呼叫`std::bind`時就啟動，並在呼叫`std::bind`之後1小時後觸發鬧鈴，而不是`setSoundB`被呼叫的時候，這是不正確的行為
要用`std::bind`實作正確的行為，就要推遲evaluate `steady_clock::now() + 1h`的時間點，將evaluate的行為用`std::bind`包裝成另一個function object就可做到
但相較於lambda，顯得十分麻煩，也不易閱讀
```cpp=
auto setSoundB = std::bind(
    setAlarm,
    std::bind(std::plus<>(), steady_clock::now(), 1h),
    _1,
    30s
);
```
注意在C++ 14後，可以用類似`std::plus<>`，不指定template parameter，這會推導為`std::plus<void>`，此時STL會根據參數推導回傳值型態
C++ 11不支援，必須要用`std::plus<steady_clock::time_point>`

如果`setAlarm`有其他overloading的function，lambda的優勢會更顯著
假設有另一`setAlarm`，多接收一個參數代表音量，如下例
```cpp=
enum class Volume { Normal, Loud, LoudPlusPlus };
void setAlarm(Time t, Sound s, Duration d, Volume v);
```
lambda版本可以正確運作，因為lambda版本是直接呼叫function，可以正確解析為3個參數的版本
但`std::bind`版本不行，因為傳入`std::bind`時，只有function name，compiler無法推斷其為3個參數的版本還是4個參數的版本
```cpp=
auto setSoundL =
    [](Sound s)
    {
        using namespace std::chrono;
        setAlarm(steady_clock::now() + 1h, s, 30s);  //ok. call 3-arg version
    };

auto setSoundB = std::bind(
    setAlarm,  // error. which setAlarm should use
    std::bind(std::plus<>(), steady_clock::now(), 1h),
    _1,
    30s
);
```
要解決此問題，必須明確指明function type，將其轉為function pointer
但由於被轉型為function pointer，此時`std::bind`行為將些許不同
產生的bind object會存function pointer，指向`setAlarm`，而不是直接呼叫該function
因此效率上可能會較差
```cpp=
using SetAlarm3ParamType = void(*)(Time, Sound, Duration);
auto setSoundB = std::bind(
    static_cast<SetAlarm3ParamType>(setAlarm),
    std::bind(std::plus<>(), steady_clock::now(), 1h),
    _1,
    30s
);
```

另外，若不了解`std::bind`實作方式，可能會對參數傳遞的方式有所誤解
1. 若用到placeholders (`_1`、`_2`...)，實際呼叫bind object時需要傳入對應的參數，該參數會用by reference方式傳遞 (因為bind object用perfect forward)
2. 若呼叫`std::bind`時，傳入一些參數做綁定，會用copy (或move，如果是rvalue)方式存入bind object的data member
3. 這在lambda中都可以明確表示，一目了然
```cpp=
// compression level on Widget
enum class CompressLevel { Low, Normal, High };

// function to make compress on Widget w
Widget compress(const Widget& w, CompressLevel level);

Widget w;

// w is passed by value to compressB
auto compressB = st::bind(compress, w, _1);

// w is captured by value to compressL
auto compressL = [w](CompressLevel level) { return compress(w, level); };

// CompressLevel::High is passed by reference
compressB(CompressLevel::High);

// CompressLevel::High is passed by value
compressL(CompressLevel::High);
```

NOTE: 若要把`Widget w`以by ref方式傳給`std::bind`，必須要`std::ref`才可以
```cpp=
// w is passed by ref to compressB
auto compressB = st::bind(compress, std::ref(w), _1);
```

`std::bind`在C++ 14中可以完全被lambda取代，但在C++ 11中依然有必要性
1. Move capture: 如[Item 32](Item-32-Use-init-capture-to-move-objects-into-closures)，`std::bind`可用在需要做move capture的情境上
2. 多型函式物件：由於bind object的`operator()`是用perfect forward將參數傳給function object，因此可以接受任何參數。如果想將bind object和function object的template版本`operator()`聯繫在一起，`std::bind`就會很有用
```cpp=
struct PolyWidget
{
    template<typename T>
    void operator()(const T& param);
};

PolyWidget w;
auto boundB = std::bind(w, _1);

boundB(1930);     // pass int to PolyWidget::operator()
boundB(nullptr);  // pass std::nullptr_t to PolyWidget::operator()
boundB("abcde");  // pass string literal to PolyWidget::operator()

// C++ 14 only
auto boundL = [w](const auto& param) { w(param); };
```

# Chapter 7. The Concurrency API

## Item 35: Prefer task-based programming to thread-based

若要非同步的執行任務，C++ 11提供兩種方式
1. thread based: 用`std::thread`
2. task based: 用`std::async`

```cpp=
int doAsyncWork();

// thread based
std::thread t(doAsyncWork);

auto fut = std::async(doAsyncWork);
```
相比於thread based，用task based可以有幾個好處
1. 由於`doAsyncWork`有回傳值，用`std::thread`沒有直接方式取得回傳值，但`std::future`有`get()` function可以取得
2. 如果`doAsyncWork`會拋出例外，也可以從`get`取得，但若用`std::thread`，程式會呼叫`std::terminate`，終止執行
3. 用`std::async`可以忽略開thread的細節，因為`std::thread`是software thread的抽象化物件，代表底層software thread的handle (e.g. pthread的`pthread_t`)
a. 若create`std::thread`超過了software thread的上限，會拋出`std::system_error`的exception (與`doAsyncWork`無關，即使`doAsyncWork`是`noexcept`也有可能)
b. 直接開thread，也有可能會oversubscript，即software thread超出hardward thread的狀況，導致需要context switch，cache flush等狀況，若要最佳化這樣的情況，要時時掌握程式的執行狀況，也需要了解許多硬體細節
4. `std::async`則是將開thead的策略交給STL，使用`std::async`不保證真的會create thread (如系統負荷高時)，此時執行`doAsyncWork`的時機點會變成呼叫`std::future::get`的時候才執行

雖然用`std::async`有許多好處，但也有適合用`std::thread`的時機
1. 需要存取底層thread實作API時：由於C++ concurrency API多是透過底層平台的API實作(e.g. pthread)，C++ concurrency API功能還不夠豐富，不足以cover底層平台如pthread的實作。若要使用pthread其他功能，還是只能用`std::thread`
2. 需要且可以最佳化thread執行狀況時：多是有使用特殊硬體的狀況時
3. 需要實作C++ concurrency API無法提供的thread framework時：例如thread pool

## Item 36: Specify std::launch::async if asynchronicity is essential

`std::async`提供一個參數`policy`表示STL應該如何執行function `f`
`policy`可以是
1. `std::launch::async`：表示function `f`必須以非同步的方式執行，也就是會create new thread執行
2. `std::launch::deferred`：表示function `f`會延遲到`std::future::get`或`std::future::wait`被呼叫時才被執行，不會create new thread，而是在呼叫`get`或`wait`的thread上執行
3. `std::launch::async | std::launch::deferred`：此為預設policy，表示交給系統決定要用哪種執行方式

預設policy使得`std::async`可能以同步或非同步的方式執行，雖然有彈性，但也會造成一些問題
```cpp=
auto fut = std::aync(f);  // suppose program creates fut in thread t1

// suppose f is run in thread t2

fut.get();  // suppose program gets the result in thread t3
```
以上情境，假設程式在`t1` 用`std::async`create了`fut`，並在`t3`執行`fut.get`，而`f`在`t2`被執行
若用預設的policy，則
1. 無法預測`t1`和`t2`是否會並行處理
2. 無法預測`t2`和`t3`是否是相同
3. 無法預測function `f`是否執行

這狀況導致此policy無法與`thread_local`並存，若function `f`中有用到`thread_local`的物件，`f`不知道他用到的是`t2`的變數，還是`t3`的變數

而如果用`std::async`後，會使用`std::future::wait_for`或`std::future::wait_until`等timeout機制的話，有可能造成程式永遠無法結束
```cpp=
using namespace std::literals;
void f();

auto fut = std::async(f);
// will wait forever if std::launch::deferred is applied
while (fut.wait_for(100ms) != std::future_status::ready)
{
    ...
}
```

解決方式是確認`fut.wait_for` (或`wait_until`)的回傳值是否為`std::future_status::deferred`，若是該值，表示結果只有在明確呼叫`get`或`wait`後才會得到
以下用`wait_for(0s)`來確認function是否被延遲執行
```cpp=
auto fut = std::async(f);

if (fut.wait_for(0s) == std::future_status::deferred)
{
    // call fut.get()
}
else
{
    while (fut.wait_for(100ms) != std::future_status::ready)
    {
        ...
    }
}
```

若想保證`std::async`一定要非同步執行，要用`std::launch::async` flag
這也可以包成一個template function
```cpp=
// C++ 11 version
template<typename F, typename... Ts>
inline std::future<typename std::result_of<F(Ts...)>::type>
reallyAsync(F&& f, Ts&&... params)
{
    return std::async(
        std::launch::async,
        std::forward<F>(f),
        std::forward<Ts>(params)...
    );
}

// C++ 14 version
template<typename F, typename... Ts>
inline auto reallyAsync(F&& f, Ts&&... params)
{
    return std::async(
        std::launch::async,
        std::forward<F>(f),
        std::forward<Ts>(params)...
    );
}
```

## Item 37: Make std::threads unjoinable on all paths

`std::thread`有joinable和unjoinable兩種狀態
unjoinable的thread可能是：
1. Default constructed `std::thread`
2. Moved `std::thread`：被move的thread不與底層thread有關聯
3. 已被joined的`std::thread`
4. 被detached的`std::thread`

`std::thread`的destructor會檢查狀態是否是joinable，如果是，會呼叫`std::terminate`強致終止程式

```cpp=
bool doWork()
{
    int var = 0;
    std::thread t(
        [&var]
        {
            // do something
            ...
        }
    );
    
    if (conditionsAreSatisfied())
    {
        t.join();
        ...
        return true;
    }
    return false;
}
```
1. thread `t`在construct之後被執行
2. 若`conditionsAreSatisfied()`是`true`，程式沒問題
3. 若是`false`，thread會在沒有被join的狀態下被destruct，會呼叫`std::terminate`

在`std::thread`沒有被join的狀態下被destruct是危險的，C++標準委員會認為此種狀況下，不論在destructor中呼叫`join()`或是`detach()`，都不正確
1. 在destructor中呼叫`join()`，會導致程式被block，造成效能問題，且這問題可能難以被追蹤
2. 在destructor中呼叫`detach()`，可能導致stack corrupt。如上例中，thread `t` capture區域變數`var`，並可能在function中修改`var`，若在離開`doWork`時`detach`，`var`被銷毀，但thread還持續使用stack上的同一塊記憶體，這會導致下一個被呼叫的function上某些變數被意外的修改

綜上所述，必須要確保當`std::thread`被銷毀前，已經被join
如果要保證在`std::thread`被銷毀前要呼叫`join`或`detach`，可以用RAII技巧包裝`std::thread`
```cpp=
class ThreadRAII
{
public:
    enum class DtorAction
    {
        Join,
        Detach,
    };
    
    ThreadRAII(std::thread&& t, DtorAction a)
        : action(a)
        , t(std::move(t))
    {
    }
    ~ThreadRAII()
    {
        if (t.joinable())
        {
            if (action == DtorAction::Join)
            {
                t.join();
            }
            else
            {
                t.detach();
            }
        }
    }
private:
    DtorAction action;
    std::thread t;
};
```
1. 在constructor中，`std::thread`參數是rvalue ref，因為`std::thread`只能move
2. destructor中，確認若t還沒有被join，就依據設定做`join`或`detach`

## Item 38: Be aware of varying thread handle destructor behavior

代表一個non-defered `std::future`與`std::thread`一樣，底層都有對應到一個software thread，但`std::future`的destructor根據狀態不同，行為會不相同

`std::future`是代表communication channel的接收端(caller端)
如下圖：
![](https://i.imgur.com/FF3wH7o.png)
1. 通常callee端會將運算的結果寫到channel的物件中(e.g. `std::promise`)，caller從`std::future`接收
2. 在中間會有一個shared state (通常在heap上)，這是因為callee和caller兩邊可能非同步執行
例如callee的運算結果可能在`std::future::get`呼叫前就算好了，其值若存在callee，在callee端物件被銷毀時結果就消失了
3. 運算結果也無法存在caller的`std::future`，因為`std::future`可能被copy (`std::shared_future`)，要存結果會不知道存在哪個`future`中
4. callee端代表物件有可能是`std::promise`，也可能是`std::async`的internal object
5. `std::future`要馬從`std::promise`取得，要馬從`std::async`的回傳值取得，因此shared state要馬是從`std::promise`產生，要馬從`std::async`產生

`future`的destructor會依據shared state的狀況有關
1. 當所有以下條件滿足時，`future`的destructor會自動`join`到非同步的thread，直到任務結束：
a. 該`future`所對應到的shared state是從`std::async`產生的
b. 該任務是非同步執行的 (明確指定`std::launch::async`、或者用預設policy且系統決定非同步執行)
c. 該`future`是最後一個指向shared state的
2. 其他狀況，destructor只是銷毀`future` object

由於`future`的API沒有提供機制來判斷shared state是否由`std::async`產生，對任意的`future` object，無法確定destructor是否會`join`

`std::future`不一定是由`std::async`產生，也可以透過如`std::package_task`產生
```cpp=
// function to run asynchronously
int calcValue();

{  // enter scope

    std::package_task<int()> pt(calcValue);

    auto fut = pt.get_future();
    
    // create thread from the package_task
    std::thread t(std::move(pt));

    // process ...
    ...
}  // leave scope
```
1. `package_task`只能move，不能copy，因此要傳給`std::thread`時，要用`std::move`
2. `fut`不是用`std::async`產生，而是從`package_task`，因此destructor不會join
3. 在create thread `t`以後直到leave scope，`t`狀態有可能是
a. `t`沒有被`join`也沒有被`detach`，此時`t`的destructor會呼叫`std::terminate`
b. `t`被`join`，此狀況不需要在`future`的destructor去`join`，因為caller端已經`join`
c. `t`被`detach`，此狀況不需要在`future`的destructor去`detach`，因此caller端已經`detach`
從上三種狀況可知，當`future`來源不是`std::async`時，所有狀況都已經由caller端處理好了，因此`future`的destructor可以不做事

## Item 39: Consider void futures for one-shot event communication

有時候，不同thread之間需要做event notification。一個thread執行到一個階段後，會通知另一條thread，讓另一thread可以繼續執行
event notification是condition variable典型使用例子
如下，假設detecting task表示該task用於偵測條件是否滿足，reacting task表示該task會接收notification並且做對應的task
```cpp=
std::condition_variable cv;
std::mutex m;

bool flag = false;

// detecting task
void detect_task
{
    ...  // detecting event
    
    // after condition hit
    {
        std::lock_guard<std::mutex> g(m);
        flag = true;  // set to true to tell reacting task something happen
    }
    cv.notify_one();  // notify reacting task
}

void react_task
{
    ...
    
    {
        std::unique_lock<std::mutex> lk(m);

        cv.wait(lk, [] { return flag; });
        // cv.wait(...) the same as following
        // while (!flag)
        // {
        //     cv.wait(lk);
        // }
        
        // react the event (when lock is held)
        ...
    }

    // react the event (when lock is not held)
    ...
}
```

1. condition variable僅用於notification，條件本身用`bool flag`表示
2. `cv.wait(lk, [] { return flag; })`是必須的，lambda表示predicated condition，設定lambda時，等同於註解的`while`迴圈
3. predicated condition是為了
a. 避免`wait`時有spurious wakeup (雖然被喚醒了，但條件其實不滿足)，因此用迴圈去check條件是否滿足，若不滿足則繼續`wait`
b. 避免條件在wait之前就已成立(detecting task在thread執行reacting task時就已notify)，此情況下去`wait`可能造成永遠不會被喚醒，因此`wait`之前要先check condition

雖然condition variable可以滿足event notification的使用，但寫起來複雜
而像上例中，detecting task其實只是想通知有event發生，沒有傳遞任何其他資料給reacting task
理論上不需要`bool flag` (`cv.notify_one`就足夠代表有event發生)
`bool flag`僅是為了應對thread之間可能發生的同步問題而存在

如果使用情境滿足以下狀況時，可以用void future代替condition variable
1. event notification只會發生一次
2. detecting task僅僅是通知有event發生，除此之外不會傳遞任何資料給reacting task

概念如下
```cpp=
std::promise<void> p;

// detecting task
...  // detecting event
p.set_value();  // notify


// reacting task
p.get_future().wait();  // wait for the notification
...  // react the event
```
1. detecting task用`std::promise`作為communication channel的寫入端，reacting task用`std::future`
2. 由於兩thread之間沒有資料傳遞，僅是通知event發生，因此`std::promise`和`std::future`的template參數是`void`。即為`std::promise<void>`和`std::future<void>`
3. 當要做notification時，呼叫`std::promise::set_value`，通知對應的`std::future`
4. reacting task用`std::future::wait`等待event發生
5. 此寫法不用處理spurious wakeup以及`wait`前就滿足條件的問題，因為`std::future::wait`都幫我們處理好了
6. 此用法缺點是僅能通知event一次，condition variable可以反覆設定`bool flag`來多次通知event，但`std::promise::set_value`只能被設定一次

以下是使用的例子，假設我們想要建立一條thread，在執行thread function前先suspend，以便於我們先做一些初始化動作
```cpp=
std::promise<void> p;

void react();  // function of reacting task

void detect()  // functino of detecting task
{
    std::thread t(
        []
        {
            p.get_future().wait();  // suspend thread t until notification
            react();  // do actual work
        }
    );
    
    ...  // do some init work.
         // At this moment thread t is suspend by std::future::wait
         // risky
    
    p.set_value();  // notify thread t.
                    // After this thread t will call react()
    
    ...  // remain work
         // risky
    
    t.join();
}
```
1. thread `t`被create出來後就會開始執行，但是會被`std::future::wait`擋住
2. create完thread `t`後，我們先進行一些初始化工作，直到`p.set_value()`被呼叫前
3. 做完後呼叫`p.set_value()`，這會讓thread `t`繼續執行
4. 最後要呼叫`t.join()`，保證thread `t`可以被join
5. 在程式中中註解兩個risky的地方，如果在此二處`detect`拋出例外，會導致thread `t`在沒有被`join`的狀態下呼叫destructor，此時程式會呼叫`std::terminate`結束 ([Item 37](#Item-37-Make-std::threads-unjoinable-on-all-paths))

可以用[Item 37](#Item-37-Make-std::threads-unjoinable-on-all-paths)介紹的`ThreadRAII`來確保thread `t`離開`detect()`的所有路徑都是unjoinable
```cpp=
void detect()
{
    ThreadRAII tr(
        std::thread(
            []
            {
                p.get_future().wait();
                react();
            }
        ),
        ThreadRAII::DtorAction::join
    );
    
    ...  // risky
    
    p.set_value();
    
    ...
}
```

用`ThreadRAII`後，不會有`std::terminate`的危險，但是卻產生另一種風險
程式中註解的risky地方，如果`detect()`在此處拋出例外，會導致`p.set_value()`不被呼叫
thread `tr`就會一直被卡在`p.get_future().wait()`
在此狀態下呼叫`ThreadRAII`的destructor，由於我們設定結束時要join，此時destructor就會永遠等下去，造成deadlock
此問題在[ThreadRAII + Thread Suspension = Trouble?](http://scottmeyers.blogspot.com/2013/12/threadraii-thread-suspension-trouble.html)有更多討論

在原先不用`ThreadRAII`的版本，可以擴充為一次notify多個thread，這只要將`std::future`改為`std::shared_future`即可
```cpp=
std::promise<void> p;
void react();

void detect()
{
    std::shared_future<void> sf = p.get_future().share();
    
    std::vector<std::thread> vt;
    for (int i = 0; i < threadsToRun; i++)
    {
        vt.emplace_back(
            [sf]  // Copy sf
            {
                sf.wait();
                react();
            }
        );
    }
    
    ...
    
    p.set_value();
    
    ...

    for (auto& t : vt)
    {
        t.join();
    }
}
```
需要注意在每個thread都需要copy `std::shared_future`

## Item 40: Use std::atomic for concurrency, volatile for special memory

`std::atomic`與`volatile`是兩種不同的目的
`std::atomic`表示對該物件的read、write或RMW皆為atomic
`volatile`無此保證
如下例子，假設Thread1與Thread2同時執行
```cpp=
std::atomic<int> ac(0);
volatile int vc(0);

/*--- Thread 1 ---*/  /*--- Thread 2 ---*/
        ++ac;                 ++ac;
        ++vc;                 ++vc;
```
當兩條thread執行結束後，`ac`的值保證是2，而`vc`則不一定，因為對`vc`的操作可能不是atomic，thread1和thread2同時寫入時，可能會互相覆蓋
e.g.
1. Thread1 read `vc` ，得到0
2. Thread2 read `vc`，得到0
3. Thread1 write `vc`，寫入1
4. Thread2 write `vc`，寫入1

另一差別是對程式碼的reorder
e.g.
```cpp=
a = b;
x = y;
```
由於兩個assignment沒有相互關係，編譯器可以任意調整順序
即使編譯器不調整，底層硬體處理時，也有可能會調整順序

使用`std::atomic`時，可以限制編譯器與硬體
```cpp=
// atomic
std::atomic<bool> valAvailable(false);
auto imptValue = compute();
valAvailable = true;

// volatile
volatile bool valAvailable(false);
auto imptValue = compute();
valAvailable = true;
```
使用`std::atomic`時，可以保證所有thread看到`imptValue`的新數值的時間點，不可以晚於`valAvailable`更新的時間點，也就是當看到`valAvailable`設為`true`時，`imptValue`已經是`compute()`的結果
用`volatile`時無此保證，順序可能被任意調動

NOTE: `std::atomic`對reorder的限制提供許多選項，預設是`std::memory_order_seq_cst`，表示不管讀寫都要一致

`volatile`用途是告訴編譯器，其處理的並不是一般的記憶體
一般的記憶體具有以下特性：
1. 當數值寫入記憶體位置後，直到被覆寫前都會保有相同的數值
如以下例子中，`x`被重複讀取，理論上`y`兩次assignment都會得到同樣的值
因此編譯器可以對此做最佳化，消除重複的read
```cpp=
int x;

auto y = x;  // read x
y = x;       // read x again
```
2. 如果數值被寫入記憶體位置後，從沒有讀取，接著再寫入另一個數值進去，就會清除原本寫入的值
如下例子，`x`被重複寫入，理論上第二次的值會覆蓋第一次，因此編譯器可以省去第一行
```cpp=
x = 10;  // write x
x = 20;  // write x again
```

```cpp=
auto y = x;
y = x;
x = 10;
x = 20;
```
編譯器可以最佳化為
```cpp=
auto y = x;
x = 20;
```

使用`volatile`會避免編譯器做這種最佳化，這可能是因為該記憶體位置實際上是一個memory mapped I/O，例如某硬體的register
```cpp=
auto y = x;  // 1st read
y = x;       // 2nd read

z = 10;  // 1st write
z = 20;  // 2nd write
```
若`x`代表溫度計的值，則第一次讀取和第二次讀取就有可能會不同
而同樣若`z`表示一個register，則對register寫入的可能就是一個命令，若省略第一次write，就有可能導致命令錯誤

這種避免最佳化重複載入貨無效儲存的特性，不適用於`std::atomic`
```cpp=
std::atomic<int> x;

std::atomic<int> y(x.load());

y.store(x.load());
```
可能被最佳化為
```cpp=
int register = x.load();
std::atomic<int> y(register);
y.store(register);
```

NOTE: `volatile`不僅是可以用在memory mapped I/O，在一些如single handler/ multiple thread上也有可能可以使用
如以下例子
```cpp=
// test.cpp
#include <thread>
#include <chrono>

int global;
std::thread t;
void func()
{
    t = std::thread(
        []()
        {
            std::this_thread::sleep_for(std::chrono::seconds(5));
            global = 1;
        }
    );
}

int main()
{
    func();
    while (global == 0)
    {
    }
}
```
若`global`不是`volatile`，當編譯時，其可能會認為`global`的值不會改變，因此對`while`做最佳化，導致無窮迴圈
以下是用`-O3`編譯後，可能產出的assemble code
在`100000c79`處，比較`global`和`0`
在`100000c80`處確認，如果兩者相等(`while`條件成立)，跳至`100000c90`
條件不成立，則繼續執行，最後會到`100000c85`執行retq，結束程式
而條件成立時跳到`100000c90`，就是進入無窮迴圈，該行指令就是反覆跳到同一個位置
```bash=
$ g++ -O3 -o test test.cpp
$ objdump --disassemble test
0000000100000c70 _main:
100000c70: 55                          	pushq	%rbp
100000c71: 48 89 e5                    	movq	%rsp, %rbp
100000c74: e8 27 00 00 00              	callq	39 <__Z4funcv>
100000c79: 83 3d f0 13 00 00 00        	cmpl	$0, 5104(%rip)
100000c80: 74 0e                       	je	14 <_main+0x20>
100000c82: 31 c0                       	xorl	%eax, %eax
100000c84: 5d                          	popq	%rbp
100000c85: c3                          	retq
100000c86: 66 2e 0f 1f 84 00 00 00 00 00       	nopw	%cs:(%rax,%rax)
100000c90: eb fe                       	jmp	-2 <_main+0x20>
100000c92: 66 2e 0f 1f 84 00 00 00 00 00       	nopw	%cs:(%rax,%rax)
100000c9c: 0f 1f 40 00                 	nopl	(%rax)
...
```

如果`global`變數是`volatile`，可能產出以下程式碼
在`100000c90`處`global`與`0`做比較
`100000c97`處，若等於`0` (`while`條件成立)，跳回`100000c90`，繼續做`cmpl`
否則繼續執行，最終執行`retq`
```bash=
$ g++ -O3 -o test main.cpp func.cpp
$ objdump --disassemble test
0000000100000c80 _main:
100000c80: 55                          	pushq	%rbp
100000c81: 48 89 e5                    	movq	%rsp, %rbp
100000c84: e8 17 00 00 00              	callq	23 <__Z4funcv>
100000c89: 0f 1f 80 00 00 00 00        	nopl	(%rax)
100000c90: 83 3d d9 13 00 00 00        	cmpl	$0, 5081(%rip)
100000c97: 74 f7                       	je	-9 <_main+0x10>
100000c99: 31 c0                       	xorl	%eax, %eax
100000c9b: 5d                          	popq	%rbp
100000c9c: c3                          	retq
100000c9d: 0f 1f 00                    	nopl	(%rax)
```

# Chapter 8. Tweaks

## Item 41: Consider pass by value for copyable parameters that are cheap to move and always copied

某一些function就是複製參數，為了效率，通常會分成兩種版本，一種接受lvalue，一種接受rvalue
有3種寫法可以達成
```cpp=
// version1
class Widget
{
public:
    void addName(const std::string& newName)
    {
        names.push_back(newName);
    }
    void addName(std::string&& newName)
    {
        names.push_back(std::move(newName));
    }

    std::vector<std::string> names;
};

// version2
class Widget
{
public:
    template<typename T>
    void addName(T&& newName)
    {
        names.push_back(std::forward<T>(newName));
    }

    std::vector<std::string> names;
};
```
1. 傳統方式會分成兩種function。缺點是要維護兩種僅有些微差異的function
不論何種function，在caller的參數傳到function時，都會bind在`newName` reference，不會有成本
而要複製進`names`時：
傳入lvalue時，會copy 1次
傳入rvalue時，會move 1次
2. 用universal reference，雖然只要維護一份function，但是用universal ref就需要維護template function，而且使用universal ref，使得function可以接受任意參數
同第1種方式，caller參數傳遞時，沒有成本
要複製進`names`時：
傳入lvalue時，會copy 1次
傳入rvalue時，會move 1次

本同款提供第3種方式，用by value方式傳遞參數
```cpp=
// version 3
class Widget
{
public:
    void addName(std::string newName)
    {
        names.push_back(std::move(newName));
    }
};
```
1. 此方式相較前兩種，程式碼只有一份，且維護也將對容易
2. 如果caller傳lvalue，在參數傳遞時會copy 1次，如果傳rvalue，會move 1次
而要複製進`names`時，則會再move 1次。因此
傳入lvalue時，會copy 1次、move 1次
傳入rvalue時，會move 2次
此方式總計會比前兩種方式多出1次move
3. 只有當參數是copyable的，才考慮此方法，因為如果只能move的參數，就不需要考慮那麼多，只要用move就好
```cpp=
class Widget
{
public:
    void setPtr(std::unique_ptr<std::string>&& ptr)
    {
        p = std::move(ptr);
    }
    std::unique_ptr<std::string> p;
}
```
4. 只有當move成本很低時，才考慮此方法。因為此方法不論如何，都會多一次move，如果成本太高，就不如第一種方法
5. 只有在參數一定會被複製時，才考慮此方法。如果function會根據某些條件決定是否複製，就表示當決定不複製時，function就必須要負責destruct物件，function就必須付出destruct的成本，這是前兩種方式不需付出的代價
```cpp=
class Widget
{
public:
    void addName(std::string newName)
    {
        if (name.length() >= 10)
        {
            names.push_back(std::move(newName));
        }
        
        // if not hit, newName needs to be destructed by addName()
    }
};
```

若複製的方式是用construct，則滿足上述條件，可以使用，成本就是會多1次move
但若複製方式是assigment，則需要再分析，如下例子
```cpp=
class Password
{
public:
    Password(std::string pwd)
        : text(std::move(pwd))
    {
    }
    
    void changeTo(std::string newPwd)
    {
        text = std::move(newPwd);
    }
    std::string text;
};
```
constructor是用construct來copy參數到text，這在上面已分析過
function `changeTo`則是用assigment來copy參數到text
考慮以下情境
```cpp=
Password p("this_is_a_very_long_password");
std::string newPassword = "short_password";
p.changeTo(newPassword);
```
在`p.changeTo()`中，`newPassword`首先會先被copy進function
接著在function中，`newPwd`中被move給text，此動作首先會把text內部資料delete，接著才做move
對text多做一次資料釋放，在使用第一種方式時很可能是不需要的，因為原本text的長度夠長，使用本來的記憶體做copy就可以了
此狀況下，用move方式的成本包含了額外的記憶體配置(caller參數傳入)與釋放(`text`記憶體)，比傳ref成本高上許多
```cpp=
class Password
{
public:
    void changeTo(const std::string& newPwd)
    {
        // can reuse the memory in text if text.capacity() >= newPwd.size()
        text = newPwd;
    }
};
```
另外，若`text.capacity() < newPwd.size()`，兩種方式都需要釋放`text`的記憶體，速度相當

再者，by value方式容易受到slice problem的影響，如果function設計上，是接受某base class以及其derived class的話，就不能用by value方式
```cpp=
class Widget {};
class SpecialWidget : public Widget {};

// processWidget is designed to accept Widget and its derived type
// But pass by value cause slice problem
void processWidget(Widget w);

SpecialWidget sw;
processWidget(sw);
```

綜上所述，利用by value方式傳遞參數，雖然可以簡化程式碼，只維護一份function
但使用時須符合多種條件，成本才不至於太高
分析包含了使用的container本身特性，傳入的參數特徵等等
需要實際量測並證明by value確實可以得到更好效率時，才考慮使用

## Item 42: Consider emplacement instead of insertion

STL container提供兩種方式來加入新元素，插入(`push_back`、`insert`)和定位(`emplace`)
插入法提供兩種overload，一種接受lvalue，一種接受rvalue
```cpp=
template<typename T>
class vector
{
public:
    void push_back(const T& x);
    void push_back(T&& x);
};
```
傳入rvalue時，如
```cpp=
std::vector<std::string> v;
v.push_back("xyz");
```
會先用`"xyz"`create出一個暫存的`std::string`物件(rvalue)，再傳入rvalue版本的`push_back` function
接著在`push_back` function裡，將暫存物件bind到`x` reference，接著再使用move constructor把`x`複製進vector中
最後，離開`push_back`後，再呼叫destructor，把暫存物件解構

使用`emplace`的話，可以避免create與delete暫存物件：`v.emplace_back("xyz")`
`emplace_back`使用perfect forward，將`std::string` constructor所需參數直接傳入，並用之建構`vector`的元素

理論上`emplace`會比`push_back`好，但實際上卻會依據傳入的參數型態、使用的container、插入或emplace的位置等等不同因素，而有不同的效能表現，因此還是要先量測數據後再做決定
以下是大致的原則，若能滿足以下條件，幾乎可確定emplace會比插入好
1. 數值以constuct方式而非assign方式加入container
如以下例子，`"xyz"`並非加在最後面，而是加在最前面，這種方式通常會將數值move到指定位置
由於move operator需要有物件來做為source，因此肯定要有暫存物件的產生
由於`emplace`優勢在於不用產生暫存物件，此狀況下`emplace`也必須被迫產生暫存物件，變得沒有優勢
```cpp=
std::vector<std::string> v;
v.emplace(v.begin(), "xyz");
```
2. 傳入的參數型態與容器的元素形態不同
如果傳入的型態本身就和容器的元素相同，不管用`emplace`或是插入效果都是相同的
3. 當插入一個已經存在的值時，container也不會拒絕存入
這種類型的container有`std::vector`、`std::deque`等，不是這種類型的則如`std::set`
container為了確認數值是否已經存在，通常要先建立節點，再進行比較
而`emplace`版本的function就必須建立暫存物件，失去優勢

考慮是否使用`emplace`，還有兩點要注意
1. 資源管理
```cpp=
std::list<std::shared_ptr<Widget>> ptrs;
void killWidget(Widget*);

// pass shared_ptr with customized deleter
ptrs.push_back(std::shared_ptr<Widget>(new Widget, killWidget));

ptrs.emplace_back(new Widget, killWidget);
```
在呼叫`push_back`前，就已經先create暫存物件
使用`emplace`確實可以不用create暫存物件，但卻有  可能引發其他問題
a. `new Widget`後，被傳入`emplace_back`
b. 在`emplace_back`內部，再配置節點，如果失敗，會拋出例外
c. 例外傳到`emplace_back`外部，在a所new出來的raw pointer沒人可以access，造成leak

2. 和`explicit` constructor的相互關係
```cpp=
std::vector<std::regex> regexes;

// add nullptr to vector
regexes.emplace_back(nullptr);

// Cannot compile
regexes.push_back(nullptr);

// Cannot compile
std::regex r = nullptr;
```
在例子中，開發者可以用`emplace_back`把`nullptr`存入container中，但這行為不符預期，因為指標根本不是`std::regex`
而如果用`push_back`的話，會發現傳入`nullptr`會無法編譯
再進一步測試，可發現`std::regex`理論上不能用`nullptr`來初始化
這是因為`std::regex`的constructor `std::regex::regex(const char*)`有帶`explicit` keyword
而`push_back`或是用等號做初始化，屬於copy initialization，這種initialization方式不能用explicit constructor
`emplace_back`則是把`nullptr`傳入function中，並用direct initialization方式產生物件
即用如`std::regex elem(nullptr)`的方式。此方式可以用explicit constructor

---