Search code examples
c++templatesinheritance

How to make a std::shared_ptr type argument convert to Base Class automatically?


#include <utility>

#include "iostream"

template<class Vty>
class Base {
public:
    Base(Vty value) : _baseValue(std::move(value)) {}

    virtual auto print() -> void {
        std::cout << "This class is Base, " << _baseValue << std::endl;
    }

protected:
    Vty _baseValue;
};

template<class Vty>
class Derived : public Base<Vty> {
public:
    Derived(Vty value) : Base<Vty>(value) {}

    auto print() -> void override {
        std::cout << "This class is Derived," << this->_baseValue << std::endl;
    }
};

template<class Vty>
auto print(const std::shared_ptr<Base<Vty>> &instance) -> void {
    instance->print();
}

int main(int argc, char **argv) {
    auto base = std::make_shared<Base<std::string>>("this is value");
    print(base);    //this line is normal.
    auto derived = std::make_shared<Derived<std::string>>("this is value");
    print(derived); //this line will report an error.
}

This code will report an error:

No matching function for call to 'print' candidate template ignored: could not match 'Base' against 'Derived'

I tried to make print like this feel:

template<template<class>class Cty, class Vty, std::enable_if_t<std::is_convertible_v<Cty<Vty>, Base<Vty>>>* = nullptr>
auto print(const std::shared_ptr<Cty<Vty>> &instance) -> void {
    instance->print();
}

Change print like this then it will be worked, but what should I do if I want to use print function like before or it's impossible? Like add some operators?


Solution

  • Template deduction happens before parameters are converted. In your case there is no deduction for Vty, because Derived<std::string> isn't Base<std::string>.

    You can explicitly pass the type to the template, and then it doesn't have to deduce it, and so the implicit pointer conversion can occur

    print<std::string>(derived);
    

    Alternately, you can explicitly convert, and allow template deduction to occur.

    print(std::static_pointer_cast<Base<std::string>>(derived));