lundi 1 août 2016

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

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<< for std::ostream and std::vector.

Consider a very generic template function which shifts its argument into a stream (really useful only with recursion, but the simple example is enough to trigger the problem):

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

This generic.h may be used throughout a project. Then in some other file, we want to output a std::vector, so we define an 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 the main file, we firstly (indirectly) use the generic.h and then, due to some other include, the 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);
}

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

The annoying thing is that clang is right and I’d like to fix this in my code. There are as far as I can see three options:

  • Move the definition of operator<< before shift(). This has the disadvantage that when including some (possibly other) files which indirectly include generic.h and vector.h, one would also have to take care to order them correctly.

  • Use a custom namespace, import everything needed from std into that namespace and define the operator on the new-namespace classes inside that namespace, so that ADL can find it.

  • Define operator<< in the std namespace. I think this is undefined behaviour.

Did I miss any option? What would be the best way in general to define overloads for functions of std-only classes (the issue does not exist if I want to shift NS::MyClass, since then I can just define the operator in NS).

Aucun commentaire:

Enregistrer un commentaire