从`std :: ostream_iterator`调用时找不到`operator<<`的重载?

时间:2014-06-08 21:18:13

标签: c++ c++11

// main.cpp

#include <iostream>
#include <utility>
#include <algorithm>
#include <iterator>
#include <map>

template<typename t1, typename t2>
std::ostream& operator<<(std::ostream& os, const std::pair<t1, t2>& pair)
{
  return os << "< " << pair.first << " , " << pair.second << " >";
}

int main() 
{
  std::map<int, int> map = { { 1, 2 }, { 2, 3 } };
  std::cout << *map.begin() << std::endl;//This works

  std::copy(
    map.begin(),
    map.end(),
    std::ostream_iterator<std::pair<int,int> >(std::cout, " ")
  ); //this doesn't work
}

  

no match for ‘operator<<’ (operand types are ‘std::ostream_iterator<std::pair<int, int> >::ostream_type {aka std::basic_ostream<char>}’ and ‘const std::pair<int, int>’)


问题

  • 我想这不起作用,因为std::copy内部的重载不可用,但为什么会这样?

2 个答案:

答案 0 :(得分:7)

解释

由于未命名的方式从命名空间std 内部调用operator<<(更具体地说,在 std :: ostream_iterator 内部) ,并且所涉及的所有参数也在同一名称空间中声明,名称空间std将搜索潜在的匹配。


Hackish解决方案

namespace std {
  template<typename t1, typename t2>
  std::ostream& operator<<(std::ostream& os, const std::pair<t1, t2>& pair)
  {
     return os << "< " << pair.first << " , " << pair.second << " >";
  }
}

注意:您只能在namespace std内专门化包含用户定义类型的模板,因此上述代码段可能会格式不正确标准(如果std::pair<T1,T2>不是用户声明的类型,请参阅this discussion)。


详细说明

下面我们有 namespace N ,它将帮助我们尝试模拟你对 namespace std 的使用,以及当编译器试图找到合适的重载时发生了什么对于给定的类型。

名称空间N

namespace N {
  struct A { };
  struct B { };

  void func (A value) { std::cout << "A"; }

  template<class T>
  void call_func (T value) { func (value); }
}

<强>的main.cpp

void func (N::B value) {
  std::cout << "B";
}

int main() {
  N::A a;
  N::B b;

  func (a);         // (1)
  func (b);         // (2)

  N::call_func (a); // (3a)
  N::call_func (b); // (3b)
}

备注

  1. 如果不了解依赖于参数的查找,可能会惊讶于编译器能够找到使( 1 )工作所需的合适重载。

    ADL声明,在函数调用中使用 unqualified-name 时,不仅会搜索当前命名空间的合适重载,还会搜索参数的命名空间;这就是编译器找到N::func的方式,即使我们没有明确写出来。

  2. 我们在当前命名空间中有一个合适的重载;它在引擎盖上都很好。

  3. ...

    为什么(3a)会编译,而(3b)会导致讨厌的诊断?

    当我们实例化模板N::call_func<T>时,它会尝试将T类型的参数传递给名为func非限定函数。

    由于 name-lookup 的规则表示当前命名空间所涉及参数的命名空间,因此我们在调用函数时会搜索合适的匹配项如果T名称空间N中声明的类型,则非限定名称将搜索名称空间N EM>

    命名空间N 中都声明了N::AN::B,因此编译器将搜索任何其他范围以查找合适的重载;这就是查找失败的原因。

答案 1 :(得分:1)

虽然这个问题已经得到解答,但我只是想补充说,有一种更好的方法来打印地图而不会滥用复制功能。 还有一个转换函数,更适合做这样的事情。 我重写了你的例子,给你一个提示,你可以使用transform函数将map转换成字符串并将它们打印到std :: cout:

#include<iostream>
#include<map>
#include<algorithm>
#include<sstream>

template<typename t1, typename t2>
 std::ostream& operator<<(std::ostream& os, const std::pair<t1, t2>& pair)
 {
     return os << "< " << pair.first << " , " << pair.second << " >";
 }

std::string toString(const std::pair<int, int>& pair) {
    std::ostringstream str;
    str << "<" << pair.first << ", " << pair.second << ">";
    return str.str();
}

int main()
 {
  std::map<int, int> map = { std::make_pair(1, 2), std::make_pair(2, 3)};
  std::cout << *map.begin() << std::endl;//This works
  std::transform(map.begin(), map.end(),
  std::ostream_iterator<std::string>(std::cout, "\n"), toString);
}