Question

I would like to define the operator << to operate on a sequence of elements, in a way like the STL algorithms work, by taking as arguments the first and last element of a container. As opposed to taking only one argument, the container itself, e.g.

std::ostream& operator<< ( std::ostream &out, std::list inList );

So that I would only have to write one function that would work regardless of if I am using list, vector, array, etc. And I would have to call the function with two arguments, inList.begin() and inList.end()

The problem is that operator<< takes only one argument. What is the best way to overcome this?

EDIT: Thank you for your answers. I should probably have made it more clear that I would prefer to have the ability to print a range of elements, including possibly a subsequence of a container (again, like STL algorithms). E.g. if a vector v has 5 elements, I wish could print them all giving a sequence from v.begin() to v.end() with an output like this:

[element1 element2 element3 element4 element5]

But I wish I also could print the first three only, in the range [v.begin(), v.begin()+3)

[element1 element2 element3]

Would your suggested answers work in this case?

Was it helpful?

Solution

You cannot get away from passing one argument, but you want something to work with any kind of container. So ideally you would want a function like

template <typename T>
std::ostream& someFunction(std::ostream& out, T first, T last) {
  // do your stuff
}

and an ostream& operator<< looking something like

template <T>
std::ostream& << (std::ostream& out, const T& sequence) {
  return someFunction(out, sequence.begin(), sequence.end());
}

However, this operator will clash with predefined ones, so you cannot just implement it like that. Fortunately you can play some template magic to get around this, and to see a very nice solution, check out this SO question and the corresponding github project

Note that since (presumably) the objective is to have a generic ostream& operator<< for containers, and you would achieve this via a function template, it is not essential to have the function take iterators. It could also work taking a (const) reference to the container.

OTHER TIPS

Pass only one argument, and take begin and end inside of the method. All std containers have begin and end. And no, you can't change the number of parameters to operator <<.

This is an old question but I had to address the same issue recently so thought I'd post my solution here. Range and makeRange duplicate the functionality of std::pair and std::make_pair, but I added the former just to avoid conflicts (other types of pairs may be printed differently).

#include <iostream>

using namespace std;

template <typename Iterator>
struct Range
{
  Range();
  Range(Iterator begin_, Iterator end_) : begin(begin_), end(end_) {}

  Iterator begin, end;
};

template <typename Iterator>
Range<Iterator>
makeRange(Iterator begin, Iterator end)
{
  return Range<Iterator>(begin, end);
}

template <typename InputIterator>
ostream &
operator<<(ostream & out, Range<InputIterator> const & range)
{
  for (InputIterator iter = range.begin; iter != range.end; ++iter)
  {
    if (iter != range.begin) out << ", ";  // adjust formatting to taste
    out << *iter;
  }

  return out;
}

int
main(int argc, char * argv[])
{
  int a[] = { 1, 2, 3, 4, 5 };
  cout << makeRange(a, a + 2) << endl;  // prints 1, 2
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top