我偶然发现了一些我最初无法解释的奇怪行为(见ideone):
#include <iostream>
#include <sstream>
#include <string>
int main() {
std::cout << "Reference : "
<< (void const*)"some data"
<< "\n";
std::ostringstream s;
s << "some data";
std::cout << "Regular Syntax: " << s.str() << "\n";
std::ostringstream s2;
std::cout << "Semi inline : "
<< static_cast<std::ostringstream&>(s2 << "some data").str()
<< "\n";
std::cout << "Inline : "
<< dynamic_cast<std::ostringstream&>(
std::ostringstream() << "some data"
).str()
<< "\n";
}
给出输出:
Reference : 0x804a03d
Regular Syntax: some data
Semi inline : some data
Inline : 0x804a03d
令人惊讶的是,在最后一次演员表中我们有地址,而不是内容!
为什么会这样?
答案 0 :(得分:19)
表达式std::ostringstream()
创建一个临时的,operator<<
取const char*
作为参数是一个自由函数,但是这个自由函数不能在临时函数上调用,因为它的类型是函数的第一个参数是std::ostream&
,它不能绑定到临时对象。
话虽如此,<<std::ostringstream() << "some data"
解析为对void*
重载的成员函数的调用,该函数打印了地址。请注意,可以在临时函数上调用成员函数。
为了调用自由函数,你需要将临时(即rvalue)转换为左值,这是你可以做的一个技巧:
std::cout << "Inline : "
<< dynamic_cast<std::ostringstream&>(
std::ostringstream().flush() << "some data"
).str()
<< "\n";
也就是说,std::ostringstream().flush()
返回std::ostream&
,这意味着现在可以调用自由函数,将返回的引用作为第一个参数传递。
此外,您不需要在这里使用dynamic_cast
(这很慢,因为它在运行时完成),因为对象的类型是众所周知的,因此您可以使用{{1 (这在编译时很快):
static_cast
应该可以正常工作。
答案 1 :(得分:9)
临时无法绑定到对非const形式参数的引用。
因此,非成员<<
未被提取。
您将获得void*
版本。
C ++ 11通过添加非成员右值流插入器函数来修复此问题,
<强> C ++ 11 强>
§27.7.3.9Rvalue流插入
[ostream.rvalue]
template <class charT, class traits, class T>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os, const T& x);
1 效果:os << x
2 返回:os
干杯&amp;第h
答案 2 :(得分:4)
要开始使用,最简单的解决方案是获取编译器考虑的可能重载列表,例如trying:
X x;
std::cout << x << "\n";
其中X
是一种没有任何流量过载的类型,它产生以下可能的重载列表:
prog.cpp: In function ‘int main()’:
prog.cpp:21: error: no match for ‘operator<<’ in ‘std::cout << x’
include/ostream:112: note: candidates are: std::ostream& std::ostream::operator<<(std::ostream& (*)(std::ostream&))
include/ostream:121: note: std::ostream& std::ostream::operator<<(std::basic_ios<_CharT, _Traits>& (*)(std::basic_ios<_CharT, _Traits>&))
include/ostream:131: note: std::ostream& std::ostream::operator<<(std::ios_base& (*)(std::ios_base&))
include/ostream:169: note: std::ostream& std::ostream::operator<<(long int)
include/ostream:173: note: std::ostream& std::ostream::operator<<(long unsigned int)
include/ostream:177: note: std::ostream& std::ostream::operator<<(bool)
include/bits/ostream.tcc:97: note: std::ostream& std::ostream::operator<<(short int)
include/ostream:184: note: std::ostream& std::ostream::operator<<(short unsigned int)
include/bits/ostream.tcc:111: note: std::ostream& std::ostream::operator<<(int)
include/ostream:195: note: std::ostream& std::ostream::operator<<(unsigned int)
include/ostream:204: note: std::ostream& std::ostream::operator<<(long long int)
include/ostream:208: note: std::ostream& std::ostream::operator<<(long long unsigned int)
include/ostream:213: note: std::ostream& std::ostream::operator<<(double)
include/ostream:217: note: std::ostream& std::ostream::operator<<(float)
include/ostream:225: note: std::ostream& std::ostream::operator<<(long double)
include/ostream:229: note: std::ostream& std::ostream::operator<<(const void*)
include/bits/ostream.tcc:125: note: std::ostream& std::ostream::operator<<(std::basic_streambuf<_CharT, _Traits>*)
首先扫描此列表,我们可以注意到char const*
明显不存在,因此合理选择void const*
并因此打印地址是合乎逻辑的。
乍一看,我们注意到所有重载都是方法,并且此处不会出现单个自由函数。
问题是引用绑定的问题:因为临时无法绑定到对非const的引用,所以形式std::ostream& operator<<(std::ostream&,X)
的重载将被彻底拒绝,并且只保留成员函数。
就我而言,它是C ++中的设计错误,毕竟我们在临时执行变异成员函数,这需要对对象进行(隐藏)引用:x
解决方法,一旦你理解了什么出错,is relatively simple并且只需要一个小包装器:
struct Streamliner {
template <typename T>
Streamliner& operator<<(T const& t) {
_stream << t;
return *this;
}
std::string str() const { return _stream.str(); }
std::ostringstream _stream;
};
std::cout << "Inline, take 2: " << (Streamliner() << "some data").str() << "\n";
打印预期结果。