我遇到了一種情況,我認為編譯器顯然能夠進行模板引數推導,但顯然不能。我想知道為什么在這種情況下我必須給出明確的模板引數。這是正在發生的事情的簡化版本。
我有一個繼承層次結構,其中基類是模板化的,派生類是基類的具體實作:
template <typename T> class Base {};
class Derived : public Base<int> {};
然后我有一個模板函式,它接受基類的共享指標:
template <typename T> void DoSomething(const std::shared_ptr<Base<T>> &ptr);
當我嘗試呼叫此方法時,我必須顯式提供模板引數:
std::shared_ptr<Derived> d = std::make_shared<Derived>();
DoSomething(d); // doesn't compile!
DoSomething<int>(d); // works just fine
特別是,如果我不使用顯式模板引數,我會收到此錯誤訊息:
main.cpp:23:18: error: no matching function for call to ‘DoSomething(std::shared_ptr&)’
23 | DoSomething(d);
| ^
main.cpp:10:28: note: candidate: ‘template void DoSomething(const std::shared_ptr >&)’
10 | template <typename T> void DoSomething(const shared_ptr<Base<T>> &ptr)
| ^~~~~~~~~~~
main.cpp:10:28: note: template argument deduction/substitution failed:
main.cpp:23:18: note: mismatched types ‘Base’ and ‘Derived’
23 | DoSomething(d);
| ^
更讓我困惑的是,如果我不使用模板引數,則可以推斷出shared_ptr:
template <typename T> void DoSomethingElse(const Base<T> &b);
Derived d;
DoSomethingElse(d); // doesn't need explicit template args!
所以很明顯我需要為DoSomething. 但我的問題是,為什么?為什么在這種情況下編譯器不能推匯出模板?Derived實作Base<int>并且型別可以推斷出來DoSomethingElse,那么為什么堅持它會shared_ptr改變編譯器找出T應該是的能力int呢?
重現問題的完整示例代碼:
#include <iostream>
#include <memory>
template <typename T> class Base {};
class Derived : public Base<int> {};
template <typename T> void DoSomething(const std::shared_ptr<Base<T>> &ptr)
{
std::cout << "doing something" << std::endl;
}
template <typename T> void DoSomethingElse(const Base<T> &b)
{
std::cout << "doing something else" << std::endl;
}
int main()
{
std::shared_ptr<Derived> d = std::make_shared<Derived>();
// DoSomething(d); // doesn't compile!
DoSomething<int>(d); // works just fine
Derived d2;
DoSomethingElse(d2); // doesn't need explicit template args!
return 0;
}
uj5u.com熱心網友回復:
Derived是 的派生類Base<int>,但std::shared_ptr<Derived>不是 的派生類std::shared_ptr<Base<int>>。
所以如果你有表單的功能
template <typename T> void f(const Base<T>&);
并且您將Derived值傳遞給它,編譯器將首先注意到它無法與 匹配Base<T>,Derived然后嘗試與.Base<T>的基類匹配Derived。然后成功,因為Base<int>它是基類之一。
如果你有表格的功能
template <typename T> void f(const std::shared_ptr<Base<T>>&);
并且你傳遞了一個std::shared_ptr<Derived>,那么編譯器將無法匹配它std::shared_ptr<Base<T>>,然后嘗試使用std::shared_ptr<Derived>. 如果后者根本有任何基類,它們是標準庫內部的,與無關,std::shared_ptr<Base<T>>因此推導最終失敗。
您在這里要求編譯器執行的操作是說:“啊哈!std::shared_ptr<Derived>可以轉換為std::shared_ptr<Base<int>>與函式引數匹配的 !” 但是編譯器不會這樣做,因為一般來說,編譯器無法使用任何演算法來列出可以將給定型別轉換為的所有型別。
相反,您必須通過明確告訴編譯器要轉換為什么來幫助編譯器。這可以這樣做:
template <typename T>
Base<T> get_base(const Base<T>*); // doesn't need definition
template <typename T> void DoSomething(const std::shared_ptr<Base<T>> &ptr);
template <typename D, typename B = decltype(get_base((D*)nullptr))>
void DoSomething(const std::shared_ptr<D>& ptr) {
DoSomething(static_cast<std::shared_ptr<B>>(ptr));
}
在這里,當DoSomething使用 type 的引數呼叫第二個多載時std::shared_ptr<D>,get_base輔助函式將用于確定其D自身的基類,其形式為Base<T>。然后,std::shared_ptr<D>將顯式轉換為,std::shared_ptr<Base<T>>以便可以呼叫第一個多載。最后,請注意,如果D不是 any 的派生類Base<T>,則第二個多載將從多載集中洗掉,從而可能使其他多載能夠處理引數型別。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/476408.html
