#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?
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));