Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
377 views
in Technique[技术] by (71.8m points)

c++ - 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).

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...