Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Print vector of vectors to ostream

Please consider the following code. I'm trying to output a vector of vectors to an ostream.

#include <iterator>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

template<typename T>
std::ostream &operator <<(std::ostream &os, const std::vector<T> &v) {
    using namespace std;
    copy(v.begin(), v.end(), ostream_iterator<T>(os, "\n"));
    return os;
}

int main() {
    using namespace std;
    vector<string> v1;
    cout << v1;
    vector<vector<string> > v2;
    cout << v2;
    return 0;
}

The statement where I output a vector of strings works. The one where I output a vector of vectors of strings doesn't. I'm using g++ 4.7.0. I've tried w/ & w/o the -std=c++11 flag. In C++11 mode, it gives me this line in the half-page of errors.

error: cannot bind 'std::ostream_iterator<std::vector<std::basic_string<char> >, char, std::char_traits<char> >::ostream_type {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'

I don't think I understand what it means. Could someone explain to me? I more or less know what an rvalue reference is, but I don't see why std::basic_ostream<char> wouldn't bind to std::basic_ostream<char>&&. Maybe I don't know it well enough. And is there a better way to do this?

Thanks in advance.

like image 311
Ashley Avatar asked Dec 16 '22 00:12

Ashley


1 Answers

The error you're getting is a bit misleading. When I tried to compile your program I had to dig into the template vomit quite a bit, and I ended up with what I thought was going on:

error: no match for 'operator<<' in '*((std::ostream_iterator<std::vector<std::basic_string<char> >, char, std::char_traits<char> >*)this)->std::ostream_iterator<std::vector<std::basic_string<char> >, char, std::char_traits<char> >::_M_stream << __value'

Basically, when you called the copy algorithm, it used the output iterator, which used the << operator from within namespace std. Once there, lookup dictates that it try to find an overload for the template vector<> in the std namespace (because that's where IT resides).

So what you need to do is declare your stream operator for the vector template in namespace std. Surround your code with namespace std {} and see what happens...

It should be noted that what you're doing is basically modifying std::vector<> and adding behavior to it that wasn't there before. Doing this is non-standard, undefined, and can easily get in your way. You might consider other options.


I was wrong about this being a koenig lookup thing. It's not, the issue is name hiding similar to what occurs in classes here you declare an overload of something in a base (not an override).

The standard namespace declares several '<<' operators. These are basically functions named operator <<. In essence what you have is this:

void fun(int);

namespace Test {

  void fun() { fun(3); }

}

int main() {
    Test::fun();
}

Note that you can use fun(int) from the global namespace or any namespace that does not have any function named fun in it. You can't use it from the Test namespace.

This is why your use of operator << declared globally works fine from the global namespace but not from within the std namespace. The std namespace already has things named the same thing as the overload you're trying to provide and so that overload is hidden from all things within std. If you could put a using declaration there things would be different.

like image 108
Edward Strange Avatar answered Jan 03 '23 05:01

Edward Strange