简体   繁体   中英

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

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM