模板重载ostream运算符

时间:2013-04-02 17:39:25

标签: c++ templates operator-overloading

我正在尝试打印STL容器。我想要的是打印用分隔符分隔的容器的elemets。 但是我遇到了一些问题。

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完成所有这些。不编译其中任何一个。

有人可以帮我澄清这件事吗?谢谢。

2 个答案:

答案 0 :(得分:5)

<强>的前提:

首先,代码是非法的,因为它错过了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附带的标准库的实现可能存在错误。

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

关于您的第二个问题:

  

为什么呢?模板未使用。它有关系吗?

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

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

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

关于您的第三个问题:

当在推导的上下文中至少有一个函数参数的类型中使用T时,编译器将允许从函数参数中推导出它(在这种情况下,它将推导出{{1} ,并且您不必明确指定它。

<强>结论:

总而言之:在添加必要的T = std::string语句后,您的程序的第一个和第三个版本是合法的并且有意义,而第二个版本则不是,而不是。

答案 1 :(得分:1)

  1. 发布后,您的代码不应该使用任何编译器进行编译。 你缺少包括,以及很多std::。我怀疑的内容 正在发生的是,你没有所有必要的 包括;特别是,#include <string>缺失, 并且g ++间接地接受它。这很好奇。该 问题通常是相反的:VC ++吸收了很多 额外包括。然而,有时只是部分(所以你可能会 最终了解std::string,但不了解非会员 与其相关的函数,如operator<<)。没有 然而,看到你的实际包含,很难说。

  2. 编译器只能执行重载决策 功能,而不是功能模板。在开始过载之前 解决方案,它试图用实例化实例化功能模板 正确的名字。成功的实例化导致 一个函数,它添加到重载集。但是怎么回事 应该实例化你的功能模板。它没有办法 了解T的用途。所以它没有实例化 (模板参数推导失败),并且没有找到它的实例 它进入过载集的方式。

  3. 我在这里看不到任何东西。添加后 在operator<<中缺少返回,它编译并运行 正确地在VC ++上。