std :: ostringstream печатает адрес c-строки вместо ее содержимого - PullRequest
19 голосов
/ 27 ноября 2011

Я наткнулся на странное поведение, которое сначала просто не мог объяснить (см. 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

Удивительно, но в последнем акте у нас есть адрес, а не содержание!

Почему это так?

Ответы [ 3 ]

19 голосов
/ 27 ноября 2011

Выражение std::ostringstream() создает временное значение, а operator<<, которое принимает const char* в качестве аргумента, является свободной функцией, но эта свободная функция не может быть вызвана во временном режиме, поскольку тип первого параметра функции равенstd::ostream&, который нельзя привязать к временному объекту.

Сказав это, <<std::ostringstream() << "some data" разрешает вызов функции-члена, которая перегружена для void*, которая печатает адрес.Обратите внимание, что функция-член может вызываться для временного объекта.

Чтобы вызвать функцию free, вам необходимо преобразовать временное значение (которое является rvalue) в lvalue, и вот один из приемов, которые вы можете сделать:

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

То есть std::ostringstream().flush() возвращает std::ostream&, что означает, что теперь может вызываться свободная функция, передавая возвращенную ссылку в качестве первого аргумента.

Кроме того, вам не нужно использовать dynamic_cast здесь (что медленно, как это делается во время выполнения), поскольку тип объекта довольно известен, и поэтому вы можете использовать static_cast (что быстро, как это делается во время компиляции):

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

, который должен работать просто отлично.

9 голосов
/ 27 ноября 2011

Временный не может привязаться к ссылке на неконстантный формальный аргумент.

Следовательно, не член << не выбран.

Вы получаете версию void*вместо этого.

C ++ 11 исправляет это, добавляя функцию, не являющуюся членом rvalue, ,

C ++ 11
§27.7.3.9 Вставка потока Rvalue
[ostream.rvalue]
template <class charT, class traits, class T><br> basic_ostream<charT, traits>&<br> operator<<(basic_ostream<charT, traits>&& os, const T& x);
1 Эффекты: os << x
2 Возвращает: os

Приветствия и hth.

4 голосов
/ 27 ноября 2011

Для начала самое простое решение - получить список возможных перегрузок, которые компилятор рассмотрел, например, пытается это:

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* будетвместо этого выбирается адрес, и поэтому печатается адрес.

На второй взгляд мы отмечаем, что все перегрузки методы , и что здесь не появляется ни одной свободной функции.

Эта проблема связана с привязкой ссылок: поскольку временный объект не может связываться со ссылкой на неконстантные, перегрузки формы std::ostream& operator<<(std::ostream&,X) полностью отклоняются и остаются только функции-члены.

Это так, насколькоМеня беспокоит ошибка проектирования в C ++, после всего того, что мы временно выполняем функцию-мутант, и для этого требуется (скрытая) ссылка на object: x

Обходной путь, как только вы поняли, что пошло не так, относительно проста и требует только небольшую оболочку:

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

, которая печатает ожидаемый результат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...