为什么不将shared_ptr派生到隐式转换为shared_ptr到Base

问题描述

我不明白为什么在下面的代码shared_ptr<Derived<int>>不会隐式转换为shared_ptr<Base<int>>

#include <memory>
    
template <typename T>
class Base {
};
    
template <typename T>
class Derived : public Base<T> {
};
    
template <typename T>
T foo(std::shared_ptr<Base<T>>) {
    return T{};
}
    
void main() {
    foo(std::make_shared<Base<int>>());
    foo(std::make_shared<Derived<int>>());
}

我遇到了convert std::shared_ptr<Derived> to const shared_ptr<Base>&,这似乎与我有关。我制作了函数模板会收到错误消息吗?

我得到的错误是:

E0304没有功能模板“ foo”的实例与参数列表匹配

C2664'std :: shared_ptr > foo (std :: shared_ptr >)':无法从'std :: shared_ptr >'转换参数1到'std :: shared_ptr >'

解决方法

此行为的原因是foo是模板。请注意,如果foo不是模板,一切都将正常工作:

int foo(std::shared_ptr<Base<int>>) {
    return int{};
}

但是,当foo是模板时,编译器首先需要实例化foo,并且它希望能够实例化精确匹配,因为隐式转换此时不适用。而且此实例化无法成功,因此会出错。

解决此问题的一种方法是使foo成为一个非常贪婪的模板,然后添加可转换性的其他约束。例如:

#include <memory>

template <typename T>
class Base {
public:
    using Type = T;
};

template <typename T>
class Derived : public Base<T> {
};

template <typename T>
auto foo(T) -> std::enable_if_t<std::is_convertible_v<T,std::shared_ptr<Base<typename T::element_type::Type>>>,typename T::element_type>
{    
    return typename T::element_type{};
}


int main() {
    foo(std::make_shared<Derived<int>>()); //OK
    foo(20); // error
}

请注意,我已经向基类中添加了一个Type成员。这不是严格必要的,但是可以简化代码。