Home > database >  How to create a friend function for template base class with constexpr
How to create a friend function for template base class with constexpr

Time:08-12

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

Compiler Explorer

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

  • Related