简体   繁体   English

std::ostringstream 打印 c 字符串的地址而不是其内容

[英]std::ostringstream printing the address of the c-string instead of its content

I have stumbled on a weird behavior that I just could not explain at first (see ideone ):我偶然发现了一种我一开始无法解释的奇怪行为(参见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";
}

Gives the output:给出输出:

Reference     : 0x804a03d
Regular Syntax: some data
Semi inline   : some data
Inline        : 0x804a03d

Surprisingly, in the last cast we have the address, and not the content!令人惊讶的是,在最后一次演员表中,我们有地址,而不是内容!

Why is that so ?为什么呢 ?

The expression std::ostringstream() creates a temporary, and operator<< which takes const char* as argument is a free function, but this free function cannot be called on a temporary, as the type of the first parameter of the function is std::ostream& which cannot be bound to temporary object.表达式std::ostringstream()创建了一个临时const char* ,以const char*作为参数的operator<<是一个自由函数,但是这个自由函数不能在临时函数上调用,因为函数的第一个参数的类型是std::ostream&不能绑定到临时对象。

Having said that, <<std::ostringstream() << "some data" resolves to a call to a member function which is overloaded for void* which prints the address.话虽如此, <<std::ostringstream() << "some data"解析为对打印地址的void*重载的成员函数的调用。 Note that a member function can be invoked on the temporary.请注意,可以在临时上调用成员函数。

In order to call the free function, you need to convert temporary (which is rvalue) into a lvalue, and here is one trick that you can do:为了调用自由函数,您需要将临时(即右值)转换为左值,这是您可以执行的一个技巧:

 std::cout << "Inline        : "
            << dynamic_cast<std::ostringstream&>(
                 std::ostringstream().flush() << "some data"
               ).str()
            << "\n";

That is, std::ostringstream().flush() returns std::ostream& which means, now the free function can called, passing the returned reference as first argument.也就是说, std::ostringstream().flush()返回std::ostream& ,这意味着,现在可以调用自由函数,将返回的引用作为第一个参数传递。

Also, you don't need to use dynamic_cast here (which is slow, as it is done at runtime), for the type of the object is pretty much known, and so you can use static_cast (which is fast as it is done at compile-time):此外,您不需要在这里使用dynamic_cast (这很慢,因为它是在运行时完成的),因为对象的类型几乎是已知的,因此您可以使用static_cast (它在编译时):

 std::cout << "Inline        : "
            << static_cast<std::ostringstream&>(
                 std::ostringstream().flush() << "some data"
               ).str()
            << "\n";

which should work just fine.这应该工作得很好。

A temporary cannot bind to a reference to non-const formal argument.临时不能绑定到对非常量形式参数的引用。

Therefore, the non-member << is not picked up.因此,非成员<<不会被选中。

You get the void* version instead.您将获得void*版本。

C++11 fixes this by adding a non-member rvalue stream inserter function, C++11 通过添加一个非成员右值流插入器函数来解决这个问题,

C++11 C++11
§27.7.3.9 Rvalue stream insertion §27.7.3.9 右值流插入
[ostream.rvalue] [ostream.rvalue]
template <class charT, class traits, class T>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os, const T& x);

1 Effects: os << x 1效果: os << x
2 Returns: os 2返回: os

To get the started, the simplest solution is to get the list of possible overloads that the compiler considered, for example trying this:首先,最简单的解决方案是获取编译器考虑的可能重载列表,例如尝试以下操作:

X x;
std::cout << x << "\n";

where X is a type without any overload for streaming which yields the following list of possible overloads:其中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>*)

First scanning this list, we can remark that char const* is conspiscuously absent, and therefore it is logical that void const* will be selected instead and thus the address printed.首先扫描这个列表,我们可以注意到char const*明显不存在,因此将选择void const*并因此打印地址是合乎逻辑的。

On a second glance, we note that all overloads are methods , and that not a single free function appears here.再看一眼,我们注意到所有的重载都是方法,这里没有一个自由函数出现。

The issue is a problem of reference binding: because a temporary cannot bind to a reference to non-const, overloads of the form std::ostream& operator<<(std::ostream&,X) are rejected outright and only member functions remain.问题是引用绑定的问题:因为临时不能绑定到对非常量的引用,形式std::ostream& operator<<(std::ostream&,X)重载被彻底拒绝,只剩下成员函数。

It is, as far as I am concerned, a design bug in C++, after all we are executing a mutating member function on a temporary, and this requires a (hidden) reference to the object :x就我而言,这是 C++ 中的一个设计错误,毕竟我们正在临时上执行变异成员函数,这需要对对象的(隐藏)引用:x

The workaround, once you understood what went awry, is relatively simple and only requires a small wrapper:一旦您了解出了什么问题,解决方法就相对简单,只需要一个小包装器:

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";

Which prints the expected result.打印出预期的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM