无法为派生自模板化基础的 class 中的 shared_ptr 推导出模板参数
Template arguments can't be deduced for shared_ptr of class derived from templated base
我 运行 我认为编译器显然能够进行模板参数推导,但显然不能。我想知道为什么在这种情况下我必须给出明确的模板参数。这是正在发生的事情的简化版本。
我有一个继承层次结构,其中基础 class 是模板化和派生的 classes 是基础的具体实现:
template <typename T> class Base {};
class Derived : public Base<int> {};
然后我有一个模板化函数,它接受一个共享基指针 class:
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;
}
Derived
是 Base<int>
的派生 class,但 std::shared_ptr<Derived>
不是 std::shared_ptr<Base<int>>
的派生 class。
因此,如果您具有以下形式的函数
template <typename T> void f(const Base<T>&);
并且你传递一个 Derived
值给它,编译器会首先注意到它不能匹配 Base<T>
和 Derived
,然后尝试匹配 Base<T>
对 Derived
的基数 class。然后这就成功了,因为 Base<int>
是基础 classes 之一。
如果你有一个函数
template <typename T> void f(const std::shared_ptr<Base<T>>&);
并且你传递了一个 std::shared_ptr<Derived>
,那么编译器将无法将其与 std::shared_ptr<Base<T>>
匹配,然后尝试使用 std::shared_ptr<Derived>
的基数 classes。如果后者有任何基础 classes,它们是标准库内部的,并且 不 与 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
重载被调用时带有类型 std::shared_ptr<D>
的参数,get_base
辅助函数将用于确定 class 的基数D
本身具有 Base<T>
的形式。然后,std::shared_ptr<D>
将显式转换为 std::shared_ptr<Base<T>>
,以便可以调用第一个重载。最后,请注意,如果 D
不是任何 Base<T>
的派生 class,则第二个重载将从重载集中删除,可能会启用其他一些重载来处理参数类型。
我 运行 我认为编译器显然能够进行模板参数推导,但显然不能。我想知道为什么在这种情况下我必须给出明确的模板参数。这是正在发生的事情的简化版本。
我有一个继承层次结构,其中基础 class 是模板化和派生的 classes 是基础的具体实现:
template <typename T> class Base {};
class Derived : public Base<int> {};
然后我有一个模板化函数,它接受一个共享基指针 class:
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;
}
Derived
是 Base<int>
的派生 class,但 std::shared_ptr<Derived>
不是 std::shared_ptr<Base<int>>
的派生 class。
因此,如果您具有以下形式的函数
template <typename T> void f(const Base<T>&);
并且你传递一个 Derived
值给它,编译器会首先注意到它不能匹配 Base<T>
和 Derived
,然后尝试匹配 Base<T>
对 Derived
的基数 class。然后这就成功了,因为 Base<int>
是基础 classes 之一。
如果你有一个函数
template <typename T> void f(const std::shared_ptr<Base<T>>&);
并且你传递了一个 std::shared_ptr<Derived>
,那么编译器将无法将其与 std::shared_ptr<Base<T>>
匹配,然后尝试使用 std::shared_ptr<Derived>
的基数 classes。如果后者有任何基础 classes,它们是标准库内部的,并且 不 与 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
重载被调用时带有类型 std::shared_ptr<D>
的参数,get_base
辅助函数将用于确定 class 的基数D
本身具有 Base<T>
的形式。然后,std::shared_ptr<D>
将显式转换为 std::shared_ptr<Base<T>>
,以便可以调用第一个重载。最后,请注意,如果 D
不是任何 Base<T>
的派生 class,则第二个重载将从重载集中删除,可能会启用其他一些重载来处理参数类型。