C++:模板的非推斷語境與std::type_identity
乍一看這個標題很玄乎,但是其實這只是涉及一個很簡單的CPP的模板推導的知識點。
筆者近期進行CPP開發工作時,在編譯時遇到了如下的模板型別的推斷錯誤:note: candidate template ignored: deduced conflicting types for parameter T (long long vs. long int)。透過一番梳理之後總結成文,希望對大家有所幫助。
1.非推斷語境
眾所周知,函式模板的使用是C++編譯期進行型別推導的過程。透過分析原始碼之中函式實參的型別,進一步推斷出呼叫的函式引數的型別,從而自動生成對應的函式,來達到精簡程式碼邏輯的效果。
而所謂非推斷語境呢?則是模板的型別不參與模板實參推導,取而代之地使用可在別處推導或顯式指定的模板實參。
單看上述文字可能很難理解,我們們直接看程式碼就能明白了。
2.舉個例子
我們先來看看下面的一段簡單的程式碼:
template<typename T>
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的編譯報錯我們可以看出,這裡出現了錯誤的模板推斷問題。模板函式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好~~~)
希望大家能夠有所收穫,筆者水平有限。成文之處難免有理解謬誤之處,歡迎大家多多討論,指教。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69999013/viewspace-2770651/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C++霧中風景17:模板的非推斷語境與std::type_identityC++IDE
- c/c++ 模板 型別推斷C++型別
- C++(std::vector)C++
- C++ 標準庫 std::set std::multiset swap()的使用C++
- C++ 模板與STLC++
- C++中std::allocator的使用C++
- C++語言中std::array的神奇用法總結,你需要知道!C++
- C++ auto 型別推斷注意的地方C++型別
- c++ std::vector 切記C++
- 【C++併發實戰】(三) std::future和std::promiseC++Promise
- 肯定賦值斷言與非空斷言賦值
- 模板語法 if 與 with 的區別
- C++,std::shared_future的使用C++
- `std::packaged_task`、`std::thread` 和 `std::async` 的區別與聯絡Packagethread
- C++(std::cout 處理 char*)C++
- C++ 11 新特性之型別推斷與型別獲取C++型別
- C++模板函式實現型別推導C++函式型別
- C++基礎::非型別模板引數在STL中的應用C++型別
- C++ 條件與 If 語句:掌握邏輯判斷與流程控制精髓C++
- 因果推斷與中介效應
- C++——模板C++
- C++ 智慧指標詳解: std::unique_ptr 和 std::shared_ptrC++指標
- 使用Boost對非值語義的C++類實現noncopyableC++
- 現代c++與模板超程式設計C++程式設計
- c++ 模板類C++
- c++模板類C++
- C++/C++11中std numeric limits的使用C++MIT
- std::reserve和std::resize的區別
- C++ primer 模板與泛型程式設計C++泛型程式設計
- C++中的模板(templates) (轉)C++
- C++標準模板庫(STL)迭代器的原理與實現C++
- makefile--if條件判斷語句的語法與使用
- C++ std::call_once 實現單例模式C++單例模式
- 判斷負環模板
- (C++11/14/17學習筆記):std::atomic續、std::async與std::thread對比C++筆記thread
- 【C++ 泛型程式設計01:模板】函式模板與類别範本C++泛型程式設計函式
- Java語言與C++語言的差異總結JavaC++
- c++ 模板模板引數("Template Template Parameters")C++