Best way to specialise operator<< for std::ostream and std::vector with generic template functions?

前端 未结 2 1665
广开言路
广开言路 2021-01-03 03:35

I am having trouble with the two-phase look-up as specified by the standard and (correctly) implemented by clang in connection with an overload of operator<<

相关标签:
2条回答
  • 2021-01-03 04:06

    Don't overload operators for types you don't control, such as:

    std::ostream& operator<<(std::ostream& s, std::vector<int> const& v);
    

    Instead create a tiny adaptor class and define the operator for that, for example:

    template<typename T> struct PrintableVector {
      std::vector<T> const* vec;
    }
    
    template<typename T>
    std::ostream& operator<<(std::ostream& s, PrintableVector<T> v) {
      for(auto const& elem : *v.vec) { s << elem << ", "; }
      return s;
    }
    

    That can be used like:

    shift(std::cout, PrintableVector<int>{&v});
    

    You can put the adaptor in whatever namespace you like, and put the overloaded operator in the same namespace so it can be found by ADL.

    That avoids lookup problems, doesn't require adding anything to namespace std, and doesn't try to uniquely define what it means to print a vector<int> (which might cause problems in other parts of the program if some other code assumes vectors are not printable, or tries to define its own overloads for them).

    0 讨论(0)
  • 2021-01-03 04:16

    I followed Jonathan’s advice and used a wrapper Printable<> to define the operator<<. By making this wrapper also implicitly-convertible to the original type, I can handle both cases where only Printable<T> is printable as well as those were also T itself was printable. Code as follows:

    template<typename T>
    struct Printable {
      T const& ref;
      Printable(T const& ref) : ref(ref) { }
      operator T const& () { return ref; }
    };
    
    template<typename T>
    Printable<T> printable(T const& in) { return Printable<T>(in); }
    
    template<typename Stream, typename Arg>
    void shift(Stream& s, Arg& arg) {
      s << printable(arg);
    }
    
    #include <iostream>
    #include <vector>
    std::ostream& operator<<(std::ostream& out, Printable<std::vector<int> > const& v) {
      for(auto const& elem : v.ref) { s << elem << ", "; }
      return s;
    }
    
    struct MyClass { };
    std::ostream& operator<<(std::ostream& s, MyClass const& m) {
      return s << "MyClass\n";
    }
    
    int main() {
      std::vector<int> v{1,2,3};
      MyClass m;
    
      shift(std::cout, v);
      shift(std::cout, m);
    }
    

    This has the advantage that at the point of use in the call to shift(), I don’t have to care what sort of type my variable has. Only in the definition of operator<< for classes I have to be careful as well as when using such an operator.

    0 讨论(0)
提交回复
热议问题