C++:模板的非推斷語境與std::type_identity

乍一看這個標題很玄乎,可是其實這只是涉及一個很簡單的CPP的模板推導的知識點。ide

  筆者近期進行CPP開發工做時,在編譯時遇到了以下的模板類型的推斷錯誤:note: candidate template ignored: deduced conflicting types for parameter T (long long vs. long int)。經過一番梳理以後總結成文,但願對你們有所幫助。函數

  1.非推斷語境學習

  衆所周知,函數模板的使用是C++編譯期進行類型推導的過程。經過分析源代碼之中函數實參的類型,進一步推斷出調用的函數參數的類型,從而自動生成對應的函數,來達到精簡代碼邏輯的效果。搜索引擎

  而所謂非推斷語境呢?則是模板的類型不參與模板實參推導,取而代之地使用可在別處推導或顯式指定的模板實參。索引

  單看上述文字可能很難理解,我們直接看代碼就能明白了。ci

  2.舉個栗子作用域

  咱們先來看看下面的一段簡單的代碼:開發

  template<typename T>get

  struct TestTemplate {編譯器

  T t;

  };

  template<typename T>

  T add(TestTemplate<T>& test, T val) {

  return test.t + val;

  }

  int main() {

  TestTemplate<long> test_template{100};

  return add(test_template, 10);

  }

  在進行編譯的時候出現以下的報錯:

  note: template argument deduction/substitution failed:

  note: deduced conflicting types for parameter 'T' ('long int' and 'int')

  經過gcc的編譯報錯咱們能夠看出,這裏出現了錯誤的模板推斷問題。頁遊http://www.coubai.com模板函數add在進行類型推斷時出現了衝突,在同一個函數中,模板類型T被同時推斷爲long與int。

  咱們來分析一下頁遊模板推斷的流程。

  首先,參數test_template的類型爲TestTempalate<long>, 它做爲add函數的第一個參數傳入,此時T的類型被推導爲了long。

  接着,參數val的類型爲int, 它做爲add函數的第二個參數傳入,而此時因爲13爲int類型,因此T被推導爲int類型。

  正是由於這樣,在add函數進行模板推導的過程之中,兩個參數test與val同時參與了模板類型的推導,致使出現了上述的問題。

  咱們能夠嘗試將add函數的調用改成以下:add(test_template, 10l)。此時val也做爲參數T也被推導爲long類型,則編譯再也不報錯。

  3. 利用非推斷語境解決問題

  顯然,上面的代碼咱們但願編譯器支持將int類型自動推導爲long,而不要出現惱人的報錯。那咱們就須要利用非推斷語境來解決問題了,讓val的類型不要參與到類型推導過程之中來,那麼問題就解決了。

  模板的非推斷語境出現比較複雜,有須要的能夠參考cppreference部分的詳細解釋。咱們將利用第一種,也是最多見的非推斷語境來解決上文提到的問題。

  The nested-name-specifier (everything to the left of the scope resolution operator ::)

  簡單來講就是::左側做用域的類型,不參與模板類型的推導。

  因此上述代碼改成以下代碼,就能夠規避原先的問題了。

  template<typename T>

  struct TestTemplate {

  T t;

  };

  template<typename T> struct identity { typedef T type; };

  template<typename T>

  T add(TestTemplate<T>& test, typename identity<T>::type val) {

  return test.t + val;

  }

  int main() {

  TestTemplate<long> test_template{1000};

  return add(test_template, 10);

  }

  這裏咱們新添加了類型identity, 並利用typename identity<T>::type規避了模板的類型推斷過程,從而讓val的類型推斷直接利用了test參數的類型推斷結果,因此此時val的類型爲long,模板類型推斷也就再也不出錯了。

  正是由於非推斷語境在模板推斷中會被使用,因此C++20提供了新的trait:

  std::type_identity與std::type_identity_t來幫助咱們解決上述的問題。它們的實現與功能與上面展現的identity一致,都是利用模板的非推斷語境來規避類型推斷不一樣致使的編譯失敗問題。

  4.小結

  C++的一些模板推斷的問題經常讓人抓狂,不少時候gcc給出的一長串報錯很容易勸退萌新。本篇聊了聊筆者實際在開發中遇到的模板推斷問題出發,一步步分析報錯,但願你們對解決編譯問題有耐心,並擅用搜索引擎,功力必不唐捐。(固然,更新的C++標準也給咱們解決問題的武器庫添磚加瓦,多多學習纔是正道,平常一念:C++20好~~~)

  但願你們可以有所收穫,筆者水平有限。成文之處不免有理解謬誤之處,歡迎你們多多討論,指教。

相關文章
相關標籤/搜索