I want to create a overloaded operator<<
for a template base class, that calls the toString
function for the child class. The issue is that the toString
function of the child class is constexpr
, which mean I cannot make it a virtual function of the base class. Here is what I want to do:
template<typename T>
class Base {
public:
Base(const T& t) : m_val(t) {}
friend std::ostream & operator<<(std::ostream &os, const Base& base);
protected:
const T m_val;
};
class Child1 : public Base<SomeLiteralType1> {
public:
using Base::Base;
constexpr const char* toString() {
const char* LUT[] = {"aa", "bb"};
return LUT[m_val]; // <-- assume T can be converted to int
}
};
class Child2 : public Base<SomeLiteralType2> {
...
};
std::ostream & operator<<(std::ostream &os, const Base& base) {
// cannot access toString() since it is not defined in Base
// maybe using dynamic_cast?
}
int main() {
Child1 c1 = {...};
Child2 c2 = {...};
// I want to be able to use << directly:
std::cout<<c1<<std::endl;
std::cout<<c2<<std::endl;
}
CodePudding user response:
Use CRTP to get access to the derived class:
template <typename T, typename Derived>
class Base {
...
friend std::ostream& operator<<(std::ostream& os, const Base& base) {
return os << static_cast<const Derived&>(base).toString();
}
...
};
class Child1 : public Base<int, Child1> {
...
};
CodePudding user response:
Since c 20, virtual function can be constexpr
, so you can have:
template<typename T>
class Base {
public:
constexpr Base(const T& t) : m_val(t) {}
virtual const char* toString() = 0;
friend std::ostream & operator<<(std::ostream &os, const Base& base)
{
return os << base.toString();
}
protected:
const T m_val;
};
class Child1 : public Base<SomeLiteralType1> {
public:
using Base::Base;
constexpr const char* toString() override {
const char* LUT[] = {"aa", "bb"};
return LUT[m_val]; // <-- assume T can be converted to int
}
};
In previous version, as you don't need constexpr
in the base class anyway, you might have a virtual function and the constexpr function:
template<typename T>
class Base {
public:
constexpr Base(const T& t) : m_val(t) {}
virtual const char* virtualToString() = 0;
friend std::ostream & operator<<(std::ostream &os, const Base& base)
{
return os << base.virtualToString();
}
protected:
const T m_val;
};
class Child1 : public Base<SomeLiteralType1> {
public:
using Base::Base;
constexpr const char* toString() {
const char* LUT[] = {"aa", "bb"};
return LUT[m_val]; // <-- assume T can be converted to int
}
const char* virtualToString() override {
return toString();
}
};