问题
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.
回答1:
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.
回答2:
You need this utility library:
- Pretty-print C++ STL containers
If you want to do this yourself (so as to teach yourself), then you need to define two overloads as:
For
std::vector<T>
: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; }
For
std::vector<std::vector<T>>
:template<typename T> std::ostream &operator <<(std::ostream &os, const std::vector<std::vector<T>> &v) { using namespace std; //NOTE: for some reason std::copy doesn't work here, so I use manual loop //copy(v.begin(), v.end(), ostream_iterator<std::vector<T>>(os, "\n")); for(size_t i = 0 ; i < v.size(); ++i) os << v[i] << "\n"; return os; }
If you have these overloads, then they together will handle these cases recursively:
std::vector<int> v;
std::vector<std::vector<int>> vv;
std::vector<std::vector<std::vector<int>>> vvv;
std::vector<std::vector<std::vector<std::vector<int>>>> vvvv;
std::cout << v << std::endl; //ok
std::cout << vv << std::endl; //ok
std::cout << vvv << std::endl; //ok
std::cout << vvvv << std::endl; //ok
来源:https://stackoverflow.com/questions/10501336/print-vector-of-vectors-to-ostream