2014年9月7日 星期日

Effective Modern C++ 筆記:Type Deduction 規則

一邊讀《Effective Modern C++》一邊作筆記 = =||
太複雜了,一定會忘記

C++ 總共有三種 Type Deduction

I. template argument deduction


template < typename >
void f(ParamType param);
f(expr); // 從 expr 推導 T 跟 Param

template argument deduction 有三種 case

1. ParamType 是 pointer 或 ref,但不是 universal ref

  • 如果 expr 是 ref,把 ref 拿掉
  • pattern-match,導出  ParamType

2. ParamType 是 universal ref(就是有 && 的形式)

  • 如果 expr 是 lvalue,那 T  ParamType 都導成 lvalue ref
  • 如果 expr 是 rvalue,那就跟平常一樣(平常一樣是啥?)

3. ParamType 不是 pointer 也不是 ref

  • 如果 expr 是 ref,把 ref 拿掉
  • ref 拿掉之後,如果 expr 是 const/volatile,也拿掉
另外,array 跟 function 會退化成 pointer


II.  auto  deduction

跟 template argument deduction 幾乎一樣,除了初始化的時候對待  {...}  不一樣, auto  會把  {...}  推導成  std::initializer_list ,但 template argument 無法推導  {...} 

auto a = {1, 2, 3};  // a 是 std::initializer_list
f({1, 2, 3});        // 編譯失敗,無法推導 T

III.  decltype  deduction

什麼就是什麼

const int a = 7; // decltype(a) 就是 const int
const int& b = a; // decltype(b) 就是 const int&

 decltype  最常用來作的,就是用來傳遞「原原本本的型態」,比方說你有一個 wrapper function,需要 perfect forward,然後 return value 的型別也要一五一十的反應出來,那你的好朋友就是  decltype

template < typename F, typename ... Args >
auto func_timer(F&& f, Args&& ... args)
-> decltype(f(std::forward(args)...))
{
    auto begin = time();
    decltype(f(std::forward(args)...)) result = f(std::forward(args)...);
    std::cout << time() - begin << std::endl;
    return result;
}

func_timer(foo, 1, 2, 3);

因為  foo  的 return value 有可能是 reference,人家有可能寫

func_timer(foo, 1, 2, 3) = bar;

所以連 referenceness 也要保留,所以要用  decltype 。但那一大長串實在太麻煩,所以 C++14 提供  decltype(auto) ,讓你可以簡單

template < typename F, typename ... Args >
decltype(auto) func_timer(F&& f, Args&& ... args) {
    auto begin = time();
    decltype(auto) result = f(std::forward(args)...);
    std::cout << time() - begin << std::endl;
    return result;
}

喔,使用  decltype(auto)  的時候還有一個特例要記住。 decltype(auto)  有兩條規則:
1. 如果遇到單單一個 name 的 lvalue expression, decltype(auto)  就是那個 name 的型別
2. 如果不是單單一個 name 的 lvalue expression, decltype(auto)  帶有 ref

decltype(auto) f1() {
    int x = 0;
    return x;     // decltype(auto) 是 int,所以 f() 也是 int
}

decltype(auto) f1() {
    int x = 0;
    return (x);   // decltype(auto) 是 int&,所以 f() 也是 int&
}

2014/09/09 更新


因為有讀書有學到新東西,又感覺已經很久沒寫一些比較深的 C++ 了,所以中秋節剛好來自幹一個 variant (github repo)