Best way to specialize the << operator for std :: ostream and std :: vector using common template functions?

I am having problems with two-phase viewing specified by the standard and (correctly) implemented by clang due to overloading operator<<for std::ostreamand std::vector.

Consider a very general template function that transfers its argument to a stream (really useful only with recursion, but a simple example is enough to cause a problem):

// generic.h
template<typename Stream, typename Arg>
void shift(Stream& s, Arg& arg) { s << arg; }

This generic.h can be used throughout the project. Then in another file we want to print a std::vector, so we define the overload

// vector.h
#include <iostream>
#include <vector>
std::ostream& operator<<(std::ostream& s, std::vector<int> const& v) {
  for(auto const& elem : v) { s << elem << ", "; }
  return s;
}

And in the main file, we first (indirectly) use it generic.h, and then, due to some others, enable vector overload:

// main.cpp
#include "generic.h"
#include "vector.h"

int main() {
  std::vector<int> v{1,2,3,4,5};
  shift(std::cout, v);
}

GCC (5.4.0) ICC (16.0), clang call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup.

, clang , Id . :

  • operator<< shift(). , (, ) , generic.h vector.h, .

  • , std , ADL .

  • operator<< std. , undefined.

- ? std -only ( , NS::MyClass, NS).

+4
2

, , :

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

, :

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

:

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

, , , ADL.

, - std , vector<int> ( , , ).

+11

Printable<> operator<<. - , , Printable<T>, , T. :

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

, shift() , . operator<< , .

0

Source: https://habr.com/ru/post/1649796/


All Articles