0
votes

I do not understand why in the following code, the shared_ptr<Derived<int>> isn't implicitly converted to a 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>>());
}

I came across convert std::shared_ptr<Derived> to const shared_ptr<Base>&, which seems related to me. Am I getting an error because I made a template of the function?

The error I get is:

E0304 no instance of function template "foo" matches the argument list

C2664 'std::shared_ptr<Base<int>> foo<int>(std::shared_ptr<Base<int>>)': cannot convert argument 1 from 'std::shared_ptr<Derived<int>>' to 'std::shared_ptr<Base<int>>'

1
Can you please create a minimal reproducible example and include that in your question? Compile that and include the exact error message it produces.Ulrich Eckhardt
@Eljay It's implicitly convertible though.HolyBlackCat
@UlrichEckhardt I am not sure what more you want, i posted the entire code and the only error message i had from the compileruser
@user this code is not compilable for other reasons. This is not minimal reproducible example'.SergeyA
@user you need to use &lt; when quoting things that have <...> pairs in them, so they are not confused with HTML markup. I have edited the error messages for you so the < show correctly.Remy Lebeau

1 Answers

2
votes

The reason for this behavior is the fact that foo is a template. Note, that everything works correctly if foo is not a template:

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

However, when foo is a template, the compiler first needs to instantiate foo, and it wants to be able to instantiate an exact match, since implicit conversions do not apply at this point. And this instantiation can not succeed, thus the error.

One way to workaround this issue would be to make foo a very greedy template, and then add additional constraints of convertibility. For example:

#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
}

Note, that I have added a Type member to the base class. This is not strictly necessary, but simplifies the code.