0

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;
}
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
auzn
  • 613
  • 3
  • 14
  • `toString` is a `public` static member function, you have access to it anywhere you want with `Chlid::ToString` but `m_val` is a member variable. Which mean that your example doesn't compile. – Stiven Aug 12 '22 at 06:00
  • 1
    @Stiven my fault. only the `LUT` inside the `toString` is meant to be `static const`. I have fixed it. – auzn Aug 12 '22 at 06:02
  • Which C++ standard do you use? If you are using C++20 you can make a virtual constexpr member function – Stiven Aug 12 '22 at 06:04
  • 1
    @Stiven c++17 at most – auzn Aug 12 '22 at 06:07
  • 1
    In your example, you can't instantiate a `constexpr` child. So why bother to put `toString` in `constexpr`. An other thing, you used private inheritance, is this on purpose or your forgot to add the `public` keyword? If it's on purpose you will not be able to use polymorphism because you can't cast a `Child&` to a `Base&` – Stiven Aug 12 '22 at 06:14
  • 1
    @Stiven yes I forgot to put the public keyword for inheritance. The reason to use `constrexpr toString()` is that `Child1` object is typically used as a `constexpr` object. – auzn Aug 12 '22 at 06:19
  • Why not directly make an operator<< for Child1 instead of Base ? Since it seems that `toString` will only be defined for `Chlid1` ? – Stiven Aug 12 '22 at 06:30

2 Answers2

0

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> {
  ...
};

Compiler Explorer

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
0

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();
  }
};

Jarod42
  • 203,559
  • 14
  • 181
  • 302