【问题标题】:Overload of operator<< not found when called from std::ostream_iterator?从 std::ostream_iterator 调用时未找到运算符 << 的重载?
【发布时间】:2014-06-08 21:18:13
【问题描述】:

这个程序

// 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&lt;&lt;’ (operand types are ‘std::ostream_iterator&lt;std::pair&lt;int, int&gt; &gt;::ostream_type {aka std::basic_ostream&lt;char&gt;}’ and ‘const std::pair&lt;int, int&gt;’)

我猜这不起作用,因为我的重载在 std::copy 中不可用,但这是为什么呢?

【问题讨论】:

  • 我不知道为什么人们不赞成这个问题,它的意图很明确,尽管标题可以写得更好。
  • @Filip Roséen - refp 论坛里有很多白痴喜欢投反对票。问题在于,在第二种情况下,当实例化模板类 std::ostream_iterator. 时,在命名空间 std:: 中搜索适当的运算符函数。由于命名空间 std 中已经声明了运算符
  • @VladfromMoscow 这不是的答案;我正在做一个详细的描述。有空时我会联系你和克里斯。
  • @Filip Roséen - refp 请参阅模板实例化和名称搜索部分。
  • @VladfromMoscow 我会让你在有空的时候阅读我的答案。

标签: c++ c++11


【解决方案1】:

说明

因为operator&lt;&lt; 是从命名空间std 内部(更具体地说是在std::ostream_iterator 内部)以不合格 方式调用的,并且所有涉及的参数也都在同一个命名空间中声明,命名空间std 将被搜索潜在的匹配项。


黑客解决方案

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 内特化包含 用户定义类型 的模板,因此根据上述 sn-p 可能是不正确的符合标准(如果std::pair&lt;T1,T2&gt; 不是用户声明的类型,请参阅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); }
}

ma​​in.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&lt;T&gt; 时,它会尝试将T 类型的参数传递给名为funcunqualified 函数。

    由于 name-lookup 的规则表明当前命名空间,涉及的参数的命名空间,在我们调用函数的情况下搜索合适的匹配项如果T 是在命名空间N中声明的类型,它将仅搜索命名空间N不合格名称.

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

【讨论】:

  • 我承认失败,因为没有办法明确回答(尽管我仍然指责 OP 没有发布实际错误),但请注意 OP 的超载和你答案中的那个放在std 里面是非法的。
  • @juanchopanza 哦,快,修复!
  • @FilipRoséen-refp,嗯,这不会与“用户声明”的其他用途冲突吗?例如,“如果类 X 没有用户声明的构造函数,则没有参数的构造函数被隐式声明为默认 (8.4)。”采取像std::string 这样的东西。有了这个定义,就没有用户声明的构造函数,但是隐式添加的构造函数会发生冲突。我对我对用户声明的模棱两可的论点持开放态度(或者只是搞砸了,它实际上只是用户定义的)。无论如何,措辞看起来不错,我最好去看看实际争论这个的问题。
  • @FilipRoséen-refp,我会的,谢谢。 [std-proposals] 是我通常会去的地方。
  • @chris 我们应该删除我们的 cmets,因为它们现在已经过时,并且/或者与当前帖子无关。来参加 C++ 聊天吧; Lounge<C++>!
【解决方案2】:

虽然问题已经得到解答,但我只想补充一点,有一种更好的方法可以打印地图,而不会为此滥用复制功能。 还有一个变换函数更适合做这样的事情。 我重写了您的示例,以提示您如何使用转换函数将地图转换为字符串并将它们打印到 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);
}

【讨论】:

    猜你喜欢
    • 2013-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-23
    • 2021-10-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多