Effective Modern C++ 系列之 條款2: auto 型別推導

Effective Modern C++ 系列之 條款2: auto 型別推導

在理解模板型別推導規則後,那麼auto型別推導不是什麼問題了,除了一個奇妙的例外情況以外,auto型別推導就是模板型別推導.

<code>template<typename>
void f(ParamType param);/<typename>/<code>

而一次調用形如:

<code>f(expr);/<code>

在f調用中,編譯會根據expr來推導T和ParamType的型別. 當變量採用auto來聲明時, auto就扮演了模板中T的角色, 而變量的型別修辭詞則扮演的是ParamType的角色. 例如:

<code>auto x = 27;        (1)  x的修飾詞就是auto自身 .
const auto cx = x; (2) cx的修飾詞 const auto .
const auto& rx = x; (3) rx的修飾詞 const auto& ./<code>

在C++中auto型別推導和模板型別推導是一模一樣的(除了在一個例外情況下),可以利用模板型別推導規則模擬auto型別推導過程如下:

<code>template<typename>   //模擬auto型別推導過程.
void func_for_x(T param);

func_fox_x(27); // 推導得出param的型別就是X的型別.

template<typename>
void func_for_cx(const T param);

func_for_cx(x) // 推導得出param的型別就是cX的型別.

template<typename>
void func_for_rx(const T& param);

func_for_rx(rx); //推導得出的param的型別就是rx的型別./<typename>/<typename>/<typename>/<code>

1. auto型別推導與模板型別推導一模一樣的規則.

採用auto進行變量聲明中,型別修飾詞取代ParamType,所以也存在三種情況:

1.1 型別修飾詞是指針或引用,但不是萬能引用.

<code>auto x = 27 ;  
const auto cx = x ;
const auto& rx = x ;/<code>

1.2 型別修飾詞是萬能引用.

<code>auto&& uref1 = x;    // x的型別是int, 且是左值,所以uref1的型別是int&。
auto&& uref2 = cx; // cx的型別是const int, 且是左值. 所以uref2的型別是const int&.
auto&& uref3 = 27; // 27的型別是int, 且是右值. 所以uref3的型別是int&&./<code>

1.3 型別修飾詞既非指針也非引用.

在條款1中,數組和函數名字如何在非引用修飾詞的前提下退化成指針,也同樣適合auto型別推導

<code>const char name[] = "J. P. Briggs";  //name的型別是const char[13].
auto arr1 = name; // arr1的型別是const char*.
auto& arr2 = name; // arr2的型別const char(&)[13].

void someFunc(int, double); //someFunc是個函數,型別是void(int, double).
auto func1 = someFunc; //func1的型別是void(*)(int, double).
auto& func2 = someFunc; //func2的型別是void(&)(int, double). /<code>

2. auto型別推導與模板型別推導不同存在差異的情況.

auto聲明變量的初始化表達式使用{}時,推導所得的型別屬於std::initializer_list

<code>auto x = {1,2,3,4,3.0} //錯誤,推導不出std::initializer_list中T./<code>

模板傳入一個{}的初始化表達式,型別推導就會失敗

<code>auto x = {1,2,3}  //x的型別是std::initializer_list

template<typename>
void f(T param)

f({1,2,3,4}); // 無法推導T的型別./<typename>
/<code>

注意: auto 和 模板型別推導真正的唯一區別在於,auto會假定用大括號括起來的初始化表達式代表一個std::initializer_list,但模板型別推導卻不會.

在函數返回值或者lambda式的形象中使用auto,意思是使用模板型別推導而非auto型別推導.

<code>auto createInitList {
return {1,2,3,4}; //錯誤,無法為{1,2,3}完成型別推導.
}

std::vector
v;
...
auto resetv = [&v](const auto& newValue) { v = newValue }; //c++14
...

restv({1,2,3}); //錯誤!無法為{1,2,3}完成型別推導.
/<code>

4. 總結

  • 在一般情況下,auto型別推導和模板型別推導是一模一樣的,但是auto型別推導會假定用大括號括起來的初始化表達式代表一個std::initializer_list,但是模板型別推導卻不會.
  • 在函數返回值或lambda式的形參中使用auto,意思是使用模板型別推導而非auto型別推導.

5. 驗證代碼

<code>#include <boost>
#include <vector>
#include <iostream>

using namespace std;
using namespace boost;
using boost::typeindex::type_id_with_cvr;

int main(int argc, char** argv) {
auto&& x = 12;
std::cout << "param =" << type_id_with_cvr<decltype>().pretty_name() << std::endl;
return 0;
}
/<decltype>/<iostream>/<vector>/<boost>/<code>

本文學習《 Effective Modern C++ 》一書整理的學習筆記


分享到:


相關文章: