为什么ostream_iterator不能按预期工作?

12

无需多言,只需以下代码:

#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)); 
}
2个回答

12
问题在于名称查找找不到您的 operator<<(ostream& os, const PAIR& r)。试图调用 operator<< 的代码位于 ostream_iterator<> 内部,而后者本身位于 std 命名空间中。名称查找在 ostream_iterator<>std 命名空间内部查找正确的函数;因为两个参数都在 std 命名空间中,所以参数相关的查找在这里没有帮助。

因此,我的建议是 (1) 将您的运算符包装到 namespace std { } 中,但我记得这是未定义行为。或者 (2) 创建一个从 std::pair 继承的结构体,在您的命名空间中定义一个新类型,并使用 ADL 查找您的 operator<<()

更新:

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

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

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

如果您无法使用C++11,那么我建议使用自定义操作符。


10

这是一个常见问题:简言之,当实例化std::ostream_iterator时,您的operator<<未被识别。

在实例化时,名称查找会尝试在命名空间std中查找operator<<。会找到候选项,因此不会考虑其他命名空间(特别是不会考虑全局命名空间)。然后,重载分辨率起作用:没有任何重载与参数类型匹配,因此编译失败。请注意,参数相关查找在这里没有任何帮助,因为std::pair也位于命名空间std中。

您有两个解决方案:

  • 将您的operator<<放入namespace std {}中,尽管您应该知道,根据标准(17.4.3.1),这是非法的
  • 避免使用std::copy完成此任务,并使用std::for_each(使用“旧式”的函数对象或lambda表达式)

@icecrime,这是C++标准的缺陷吗?还是有什么理由呢? - xmllmx
@xmllmx:这就是命名空间的工作方式,我认为这不是一个缺陷。 - icecrime
1
@xmllmx: 因为参数依赖查找发生在模板实例化的时候(在有问题的情况下是在命名空间std中)。 - icecrime
@icecrime,非常感谢您的快速回答和耐心解释。 - xmllmx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接