我們先介紹一個表示引數包的輔助型別:
template<typename... T> struct Pack { };
現在,這是具有奇怪行為的函式:
template<typename... TT, typename T>
void f(Pack<TT...>, Pack<T>, std::type_identity_t<TT>..., std::type_identity_t<T>);
std::type_identity_t此處用于禁用對最后兩個引數的推導,以防引入任何歧義。
首先,我試著這樣稱呼它:
f(Pack<int>{}, Pack<int>{}, 5, 5);
GCC 引發錯誤并給出以下解釋:
<source>:12:6: note: candidate expects 3 arguments, 4 provided
12 | f(Pack<int>{}, Pack<int>{}, 5, 5);
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
這不是我預期的那種錯誤,我認為扣除會導致T = intand TT... = int。但是,好吧,讓我們按照筆記所說的做,并少提供一個論點:
f(Pack<int>{}, Pack<int>{}, 5);
它仍然給出一個錯誤,但這次是:
<source>:12:6: error: too few arguments to function 'void f(Pack<TT ...>, Pack<T>, std::type_identity_t<TT>..., std::type_identity_t<T>) [with TT = {int}; T = int; std::type_identity_t<T> = int]'
10 | f(Pack<int>{}, Pack<int>{}, 5);
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
現在他們太少了。另請注意,此錯誤訊息證實了我對推導的T和的假設TT...。
此時我切換到 Clang 以查看它是否會編譯此代碼,但上述兩個呼叫f(帶有 3 個和 4 個引數)都會導致相同的錯誤,并注意:
<source>:8:6: note: candidate template ignored: deduced packs of different lengths for parameter 'TT' (<int> vs. <>)
void f(Pack<TT...>, Pack<T>, std::type_identity_t<TT>..., std::type_identity_t<T>);
^
我還嘗試使用 MSVC 編譯代碼,它確實編譯沒有錯誤。
到底是怎么回事?有什么東西使這段代碼無效嗎?由于奇怪的錯誤訊息,它是編譯器錯誤嗎?
uj5u.com熱心網友回復:
std::type_identity<TT>...在評論中提到作為從引數推導中洗掉第三個函式引數(這是一個包)的一種方法,但這沒有效果,因為沒有出現在引數串列末尾的函式引數包位于非無論如何推斷背景關系;根據[temp.deduct.type]/5.7:
/5 未推斷的背景關系是:
- [...]
- /5.7 不出現在引數宣告串列末尾的函式引數包。
那么為什么不能從第一個函式引數(Pack<TT...>)中明確推匯出引數包呢?這對于 C 開發人員來說可能是有意義的,但是它可能最終成為實作者的實作質量問題,因為它將開始將模板引數推導與多載決議混合在一起。這將需要兩次模板引數推導,其中首先明確推匯出一個引數包,然后從一個函式引數中推匯出,然后使用結果修改同一函式的引數串列(以擴展其他不可推導的引數包),然后回傳到修改后的函式模板的模板引數推導。這不是模板引數推導在其他任何地方的作業方式,可能僅僅是由于實作的質量。
[temp.deduct.call]/1確實提到從未推斷出這樣的包(強調我的):
[...]當函式引數包出現在非推導背景關系([temp.deduct.type])中時,永遠不會推導該包的型別。
可以爭論這是否故意拒絕可以從其他任何地方推斷出來的包。如果是這樣,則 OP 的程式確實格式錯誤,但是編譯器的錯誤訊息對于將其診斷為根本原因(如果是)并沒有多大幫助。似乎編譯器可能依賴 [temp.deduct.call]/1 將不可演繹的模板引數包實際推匯出為空包。
我們最終可能會注意到 [temp.deduct.call]/1 支持使用顯式模板引數,因為這不是推導:
#include <type_traits>
template<typename... T> struct Pack { };
// Note the swap of template argument positions
// to allow explicitly providing template arguments.
template<typename T, typename... TT>
void f(Pack<TT...>, Pack<T>, std::type_identity_t<TT>..., std::type_identity_t<T>) {}
int main() {
f<int, int, int>(Pack<int, int>{}, Pack<int>{}, 1, 2, 3);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/491227.html
上一篇:std::derived_from在取消參考的迭代器上給出參考型別的問題
下一篇:模糊類模板轉換
