【问题标题】:Why does ostream_iterator not work as expected?为什么 ostream_iterator 不能按预期工作?
【发布时间】:2010-12-15 07:55:23
【问题描述】:

下面的代码不用多说:

#include <utility>
#include <vector>
#include <iostream>
#include <iterator>

using namespace std;

typedef pair<char, char> PAIR;

ostream& operator <<(ostream& os, const PAIR& r)
{
    return os << r.first;
}

int main() 
{
    vector<PAIR> coll; 

    cout << coll[0]; // OK. 

    // The following line will cause a compilation error! Why???
    copy(coll.begin(), coll.end(), ostream_iterator<PAIR>(cout)); 
}

【问题讨论】:

    标签: c++ stream


    【解决方案1】:

    问题是名称查找找不到您的operator&lt;&lt;(ostream&amp; os, const PAIR&amp; r)。尝试调用operator&lt;&lt; 的代码位于ostream_iterator&lt;&gt; 内的某个位置,而std 本身位于std 命名空间内。名称查找在 ostream_iterator&lt;&gt; 和 std 命名空间内寻找正确的函数;依赖于参数的查找在这里没有帮助,因为这两个参数也在 std 命名空间中。

    所以,我的建议是 (1) 要么将您的运算符包装到 namespace std { },但那是 UB、IIRC。或者 (2) 创建一个继承自 std::pair 的结构以在您的命名空间中定义一个新类型,并使用 ADL 找到您的 operator&lt;&lt;()。

    更新:

    我的第三个建议是使用自定义操纵器打印出这对。

    至于我的第二个建议,如果您可以使用 C++11,那么从 std::pair 继承应该很容易(未经测试):

    struct PAIR : std::pair
    {
      using std::pair::pair;
    };
    

    如果你不能使用 C++11,那么我建议使用自定义操纵器。

    【讨论】:

      【解决方案2】:

      这是一个常见的问题:总之,在实例化std::ostream_iterator 时看不到您的operator&lt;&lt;。

      在实例化期间,名称查找会尝试在命名空间 std 中查找 operator&lt;&lt;。将找到候选者,因此不会考虑其他命名空间(尤其是全局命名空间)。然后,重载决议开始发挥作用:没有一个重载与参数类型匹配,因此编译失败。请注意,与参数相关的查找在这里没有任何帮助,因为 std::pair 也在命名空间 std 中。

      你有两个解决方案:

      • 将您的operator&lt;&lt; 附在namespace std { } 中,尽管您应该知道根据标准这是非法的 (17.4.3.1)
      • 在此任务中避免使用 std::copy 并使用 std::for_each(使用“老式”仿函数或 lambda)

      【讨论】:

      • @icecrime,这是 C++ 标准的缺陷吗?或者有什么理由吗?
      • @xmllmx:这就是命名空间的工作方式,我不认为这是一个缺陷
      • @xmllmx:因为参数相关的查找发生在模板实例化点(在有问题的情况下是在命名空间std)
      • @icecrime,非常感谢您的快速回答和耐心的解释。
      猜你喜欢
      • 2021-05-30
      • 2020-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多