6

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

1. g++ 与 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...

为什么?该模板未使用。应该有关系吗?


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

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 做这一切。不编译其中任何一个。

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

4

2 回答 2

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用于推导上下文中至少一个函数参数的类型时,编译器将允许从函数参数中推导它(在这种情况下,它将推导T = std::string,您不必显式指定它。

结论:

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

于 2013-04-02T17:49:18.663 回答
1
  1. 如发布的那样,您的代码不应使用任何编译器进行编译。您缺少包括,以及很多std::. 我怀疑 正在发生的事情是您没有所有必要的包括;特别是,那#include <string>是缺失的,而 g++ 间接地捡起了它。这很好奇。问题通常是相反的:VC++ 获得了很多额外的包含。但是,有时只是部分了解(因此您最终可能会了解std::string,但不了解与其关联的非成员函数,例如operator<<)。但是,如果没有看到您的实际包含,很难说。

  2. 编译器只能对函数执行重载解析,不能对函数模板执行重载解析。在开始重载解析之前,它会尝试用正确的名称实例化一个函数模板。成功的实例化会产生一个函数,并将其添加到重载集。但是它应该如何实例化您的函数模板。它无法知道该用于什么T。所以它没有实例化(模板参数推导失败),并且没有它的实例进入重载集。

  3. 我在这里没有立即看到任何东西。在 中添加缺少的返回后operator<<,它可以在 VC++ 上正确编译和运行。

于 2013-04-02T17:52:56.310 回答