【问题标题】:Print vector of vectors to ostream将向量的向量打印到 ostream
【发布时间】:2012-05-08 15:08:14
【问题描述】:

请考虑以下代码。我正在尝试将向量的向量输出到 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;
}

我输出字符串向量的语句有效。我输出字符串向量向量的那个不是。我正在使用 g++ 4.7.0。我试过 w/ & w/o -std=c++11 标志。在 C++11 模式下,它在错误的半页中给了我这一行。

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>&&'

我想我不明白这意味着什么。有人可以向我解释吗?我或多或少知道什么是右值引用,但我不明白为什么std::basic_ostream&lt;char&gt; 不会绑定到std::basic_ostream&lt;char&gt;&amp;&amp;。可能我还不够了解。有没有更好的方法来做到这一点?

提前致谢。

【问题讨论】:

标签: c++ vector ostream


【解决方案1】:

您遇到的错误有点误导。当我试图编译你的程序时,我不得不深入挖掘模板呕吐物,我最终得到了我认为发生的事情:

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'

基本上,当您调用复制算法时,它使用输出迭代器,该迭代器使用命名空间 std 中的 的重载(因为这是 IT 所在的位置)。

所以你需要做的是在命名空间std中为向量模板声明你的流操作符。用namespace std {} 包围你的代码,看看会发生什么......

应该注意的是,您所做的基本上是修改 std::vector 并向其添加以前不存在的行为。这样做是非标准的、未定义的,并且很容易妨碍您。您可以考虑其他选择。


我错了,这是一个 koenig 查找的事情。不是,问题是名称隐藏类似于类中发生的情况,在这里您声明基类中某些内容的重载(而不是覆盖)。

标准命名空间声明了几个“operator << 的函数。本质上,您拥有的是:

void fun(int);

namespace Test {

  void fun() { fun(3); }

}

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

请注意,您可以使用全局命名空间中的fun(int),也可以使用其中没有任何名为fun 的函数的命名空间。您不能在 Test 命名空间中使用它。

这就是为什么您使用全局声明的运算符 std 命名空间中正常工作。 std 命名空间已经拥有与您尝试提供的重载相同的名称,因此重载对 std 中的所有内容都隐藏。如果您可以使用 using 声明,情况会有所不同。

【讨论】:

  • 把东西放在标准不允许的 std 命名空间中是吗?无论如何,我非常怀疑它是否会在任何实际实现中引起问题,而且它似乎对我有用。
  • 是的,成功了!谢谢!不过,我并不完全明白。如果它可以在全局命名空间中找到vector的重载,其中T是字符串,为什么在2级查找的第2级找不到重载?是否编写了 std 命名空间中的输出迭代器代码来查找 std::operator
  • 这是未定义的行为,但你必须做你必须做的。如果它已经被定义,它已经可以工作了。
  • @Ashley - 也与通话范围有关。名称查找有点复杂,尤其是 wrt 模板。阅读有关 koenig 查找和其他名称查找规则的信息。
  • @CrazyEddie,谢谢。其他任何人,Wikipedia 都有一个很好的解释。您会看到在这种情况下不会搜索全局命名空间。
【解决方案2】:

您需要这个实用程序库:


如果你想自己做这个(以便自学),那么你需要定义两个重载为:

  • 对于std::vector&lt;T&gt;

    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;
    }
    
  • 对于std::vector&lt;std::vector&lt;T&gt;&gt;

    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;
    }
    

如果您有这些重载,那么它们将一起递归处理这些情况:

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

【讨论】:

  • 您还有什么其他措施可以让它发挥作用吗?因为它对我不起作用。 ideone.com/A2d67
  • @BenjaminLindley:我看不出它不应该工作的任何原因。也许是编译器错误?无论如何,手动循环工作正常。
  • 其他版本不起作用,因为ostream_iterator 位于std 命名空间中,vector 也是如此。因此,ADL 无法找到您定义的operator &lt;&lt; 模板,并且无法编译。
  • @DaveS:我不相信。为什么手动循环会起作用?它如何找到我定义的operator&lt;&lt; 模板?
  • @Nawaz:我的猜测是因为你的operator&lt;&lt; 在全局命名空间中,所以它也被搜索了。事实上,如果您将 OP 的原始代码更改为使用手动循环(与复制/迭代器相比),它无需任何进一步修改即可工作(ideone.com/rVBY2)。嗯...这可能是模板后期绑定的错误
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-31
  • 1970-01-01
  • 1970-01-01
  • 2021-12-20
相关资源
最近更新 更多