在OS X上,简单的C++程序给出了错误的结果(这是由于命令行选项“c++03”与“c++11”的不同造成的)。

8

这个简单的程序(在Linux上编译时)将根据是否使用-std=c++0x编译,正确地给出两个不同的答案。

问题:我无法在OS X(Mountain Lion,10.8 SDK)上复现相同的情况。我漏掉了什么?

#include <iostream>
#include <sstream>

class Thing : public std::ostringstream
{
public:
    Thing() : std::ostringstream() {}
    virtual ~Thing() { std::cerr << str(); }
};

int main(int argc, const char * argv[]) {
    Thing() << "Hello" << std::endl;
    return 0;
}

为了理解我的意思,请按照以下步骤操作(首先在Linux上操作,以便了解它应该如何工作):
> g++ main.cpp
> ./a.out
0x401471

> g++ -std=c++0x main.cpp
> ./a.out
Hello

第一个将打印十六进制地址,第二个将打印“Hello”。这是正确的行为,因为运算符<<解析为两个不同的东西(在C++03中没有rvalue引用,所以你可以这样做)。

现在,在OS X上尝试同样的事情:


> xcrun c++ main.cpp
> ./a.out
0x10840dd88

(这将正确生成十六进制输出。)
> xcrun c++ -std=c++0x main.cpp
> ./a.out
0x10840dd88

(哎呀...还是十六进制输出...我们处于C++11x模式,但也许没有使用正确的头文件?)


注意:编译器版本在这里:

> xcrun c++ --version
Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn)
Target: x86_64-apple-darwin12.2.0
Thread model: posix

注意:这不是C ++本身的问题,而是OS X构建问题。对于那些感兴趣的人,下面的答案中突出了它在使用C++03和C++11时产生不同结果的原因。


2
我很欣赏你的勇气,但在OS X上让正常的代码正确编译已经够难了,更别说遵循一个新(近)标准的边缘代码了。 - Charles
2
请注意,Mountain Lion 将使用 Clang 而不是 GCC。 - porges
#include <stream> 对我来说无法编译。你能修复一下这个包含指令吗?-- 没关系,我已经解决了。 - Keith Thompson
1
你使用的 Clang 版本是什么? - N_A
1
如果您在代码片段中包含输出结果而不仅仅是输入命令,以展示不同的结果,那将会很好。 - Barmar
2个回答

18
首先,预期的行为差异是因为operator<<(std::ostream&, const char*)重载(实际上是一个函数模板特化,但现在不要紧)具有类型为std::ostream&的参数,并且左值引用只能绑定到左值,在您的示例中流是rvalue,因此不能使用该重载。在C++03中,这意味着唯一可行的重载是std::ostream::operator<<(const void*)成员函数,因为成员函数可以调用rvalue对象,所以该字符串被写出为十六进制的void*地址。在C++11中,有一个新的operator<<(std::ostream&&, const T&)函数模板,允许向rvalue流进行写入,并转发到operator<<(std::ostream&, const char*)重载,因此将输出字符串而不是十六进制地址。
在GNU/Linux上,您可能正在使用相当新的GCC版本,它在编译器(g++)和标准库(libstdc++)中都有相当好的C++11支持,因此具有operator<<(std::ostream&&, const T&)重载,一切都很顺利。在OS X上,你可能正在使用带有GCC标准库(libstdc++)的Clang。默认情况下,Xcode附带一个古老版本的GCC(4.2),而来自GCC 4.2的标准库不支持C++11,因此没有rvalue流的operator<<重载。使用-std=c++0x告诉Clang支持C++11语言特性(如rvalue引用),但不能让GCC 4.2的库增加在发布GCC 4.2时根本不存在的C++11代码。苹果公司没有发布非史前时代的libstdc++,而是编写了自己的标准库实现来配合LLVM和Clang项目。使用-stdlib=libc++告诉Clang使用该libc++标准库实现,而不是古老的libstdc++。由于libc++是最近编写的,因此具有rvalue引用的operator<<重载。

7
似乎是clang默认使用libstdc++而不是libc++的问题。这样编译:clang++ -std=c++0x -stdlib=libc++ test.cpp 将产生预期输出。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接