Print vector of vectors to ostream

怎甘沉沦 提交于 2021-02-04 16:08:34

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!