【问题标题】:Template overloading ostream operator模板重载 ostream 运算符
【发布时间】:2013-04-02 17:39:24
【问题描述】:

我正在尝试漂亮地打印一个 STL 容器。我想要的是打印用分隔符分隔的容器的元素。 但是我遇到了几个问题。

1. g++ vs VC++

ostream& operator<<(ostream& o, const vector<string>& v) {
    copy(v.begin(), v.end(), std::ostream_iterator<string>(o,","));
}

int main()
{

    vector<string> s_v;
    s_v.push_back("one");
    s_v.push_back("two");

    cout << s_v;

}

g++(mingw32 上的 gcc 版本 4.4.0)可以编译它并且工作正常。 VC++ (Visual Studio 9) 无法编译此代码。

error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'const std::string' (or there is no acceptable conversion)
1>        c:\program files (x86)\microsoft visual studio 9.0\vc\include\ostream(653): could be 'std::basic_ostream<_Elem,_Traits> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const char *)'
1>        with
1>        [

这是为什么呢?这个代码是非法的吗?还是只是 VC++ beign VC++?


2。未使用的模板变量会中断编译。

如果现在我像这样在 ostream 中添加一个模板(没有使用,只是坐在那里)

template <typename T>  // <----- Here
ostream& operator<<(ostream& o, const vector<string>& v) {
    copy(v.begin(), v.end(), std::ostream_iterator<string>(o,","));
}

int main()
{

    vector<string> s_v;
    s_v.push_back("one");
    s_v.push_back("two");

    cout << s_v;

}

gcc 无法再匹配运算符了。

    error: no match for 'operator<<' in 'std::cout << s_v'

and a lot more candidates...

为什么?该模板未使用。重要吗?


编辑:这已解决。我不得不返回 o;

3.使用的模板

template <typename T>
ostream& operator<<(ostream& o, const vector<T>& v) {
    copy(v.begin(), v.end(), std::ostream_iterator<T>(o,","));

    return o; // Edited
}

int main()
{

    vector<string> s_v;
    s_v.push_back("one");
    s_v.push_back("two");

    vector<int> i_v;
    i_v.push_back(1);
    i_v.push_back(2);

    cout << s_v;
    cout << i_v;
}

如果我知道使用模板类型。 g++ 可以编译它,但随后以异常终止。

terminate called after throwing an instance of 'std::bad_cast'
  what():  std::bad_cast

VC++ 只是坐着看着 gcc 做这一切。不编译任何一个。

有人可以帮我澄清一下吗?谢谢。

【问题讨论】:

  • 在 std 命名空间中定义你的操作符。你会看到区别
  • 如果编译器不能推断模板参数的类型,你必须自己提供。所以如果你有template&lt;typename T&gt; void Foo() { ... },你需要使用Foo&lt;Type&gt;()。显然,当您重载运算符时,您不能这样做。当您确实使用T 时,编译器能够推断其类型,这就是它起作用的原因。
  • 另外,你在 ideone 上的最后一个例子 runs fine。不过,您可能希望将return o; 添加到您的operator&lt;&lt;。如果您执行cout &lt;&lt; s_v &lt;&lt; i_v,这很可能会使您的程序崩溃。
  • @PiotrNycz 不要std:: 中定义运算符,除非它涉及您在某处定义的类型。否则,代码是非法的。
  • @Curious 我无法重现该错误。一旦我修复了明显的错误(缺少包含、缺少std::、缺少返回),它就可以在我的系统(VC++ 11)中使用。

标签: c++ templates operator-overloading


【解决方案1】:

前提:

首先,代码是非法的,因为它错过了return 语句(这可能是导致第三版中引发异常的原因):

ostream& operator<<(ostream& o, const vector<string>& v) {
    copy(v.begin(), v.end(), std::ostream_iterator<string>(o,","));
    return o; // <== THIS ONE WAS MISSING
}

这会将未定义的行为注入您的程序。实际上,根据 C++11 标准的第 6.6.3/1 段:

[...] 从函数末尾流出相当于没有值的返回; 这会导致未定义 值返回函数中的行为

关于您的第一个问题:

一旦修复,您的代码就没有问题了,而 VC9 附带的标准库的实现可能存在错误。

事实上,编译器应该在参数的命名空间 (std) 和进行调用的命名空间(全局命名空间)中查找符合条件的 operator &lt;&lt; 重载。只要您的运算符在全局命名空间中定义并且语句cout &lt;&lt; s_v 在全局命名空间中,重载解析应该成功地选择您的重载。

关于你的第二个问题:

为什么?该模板未使用。重要吗?

这很简单:编译器无法从函数参数中推导出T,因此除非您明确指定它,否则将导致编译错误。但是,显式指定模板参数将意味着执行以下操作,这几乎是无意义的:

::operator << <void>(std::cout, s_v);

在 C++11 中,您可以为 T 指定默认参数,这将使函数调用合法,然后再次出现错误,目的是什么?

关于你的第三个问题:

T 用于推导上下文中至少一个函数参数的类型时,编译器将允许从函数参数中推导它(在这种情况下,它将推导T = std::string,并且您不必明确指定。

结论:

总结一下:在添加了必要的return 语句之后,你的程序的第一个和第三个版本是合法且有意义的,而第二个则不是并且没有。

【讨论】:

  • 关于他为第一个问题实际发布的代码:它不会用任何编译器编译,因为缺少包含。当缺少包含时,我已经看到了 VC++ 的奇怪重载解决方案:包括 &lt;iostream&gt; 可能会提供一些(但不是全部)&lt;string&gt;,所以有些东西可以工作,但有些则不行。
  • @JamesKanze:对,我假设 OP 导入了正确的标题和所有必要的标题,但确实不能确定,因为问题文本中缺少初步部分跨度>
  • 我正在使用标准;我有适当的包含。
  • 问题是退货,然后是你们俩解释的第二部分。
  • @Curious:这与您的问题无关,但是拥有using namespace std; 被认为是一种不好的编程习惯,因为它很容易导致名称冲突。你应该用using declarations(例如using std::cout)替换它/跨度>
【解决方案2】:
  1. 正如发布的那样,您的代码不应使用任何编译器进行编译。 你缺少包括,还有很多std::。我怀疑 正在发生的是你没有所有必要的 包括;特别是缺少#include &lt;string&gt;, 并且 g++ 间接地捡起它。这很好奇。这 问题通常是相反的:VC++ 吸收了很多 额外包括。然而,有时只是部分地(所以你可能 最终知道std::string,但不知道非会员 与之关联的函数,例如operator&lt;&lt;)。没有 然而,看到你的实际包含,这很难说。

  2. 编译器只能在 函数,而不是函数模板。开始超载前 分辨率,它尝试实例化一个函数模板 正确的名称。成功的实例化导致 一个函数,它添加到重载集。但是怎么样 应该实例化你的函数模板。它没有办法 知道T 的用途。所以它不会实例化 (模板参数推导失败),并且没有找到它的实例 进入重载集。

  3. 我没有立即看到任何内容。添加后 operator&lt;&lt; 中缺少返回,它编译并运行 在 VC++ 上正确。

【讨论】:

  • 我正在使用标准;我有适当的包括。然而奇怪的是它可以与 VC 一起为你工作。它仍然不适合我。第三个正版。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-10
  • 1970-01-01
  • 2021-02-17
  • 1970-01-01
  • 2021-02-12
  • 1970-01-01
相关资源
最近更新 更多