Превращение временного stringstream в c_str() в одном выражении

Рассмотрим следующую функцию:

void f(const char* str);

Предположим, что я хочу сгенерировать строку с использованием stringstream и передать ее этой функции. Если я хочу сделать это в одном из утверждений, я могу попробовать:

f((std::ostringstream() << "Value: " << 5).str().c_str()); // error

Это дает ошибку: 'str()' не является членом 'basic_ostream'. ОК, поэтому оператор < возвращает ostream вместо ostringstream - как насчет того, чтобы вернуть его обратно в ostringstream?

1) Является ли это безопасным?

f(static_cast<std::ostringstream&>(std::ostringstream() << "Value: " << 5).str().c_str()); // incorrect output

Теперь с этим вызывается вызов оператора < ('Value: "), который фактически вызывает оператор ostream < (void *) и печатает гексаговый адрес. Это неправильно, я хочу текст.

2) Почему оператор < на временном std:: ostringstream() вызов оператора ostream? Разумеется, у временного есть тип "ostringstream", а не "ostream"?

Я также могу временно использовать принудительный вызов оператора!

f(static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << "Value: " << 5).str().c_str());

Это, похоже, работает и передает значение "Значение: 5" в f().

3) Я полагаюсь теперь на поведение undefined? Отливки выглядят необычно.


Я знаю, что лучшая альтернатива - это что-то вроде этого:

std::ostringstream ss;
ss << "Value: " << 5;
f(ss.str().c_str());

... но меня интересует поведение этого в одной строке. Предположим, кто-то захотел сделать (сомнительный) макрос:

#define make_temporary_cstr(x) (static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << x).str().c_str())

// ...

f(make_temporary_cstr("Value: " << 5));

Будет ли эта функция ожидаться?

Ответ 1

Вы не можете использовать временный поток для std::ostringstream&. Он плохо сформирован (компилятор должен сказать вам, что это неправильно). Однако это может сделать следующее:

f(static_cast<std::ostringstream&>(
  std::ostringstream().seekp(0) << "Value: " << 5).str().c_str());

Это, конечно, уродливо. Но это показывает, как это может работать. seekp - это функция-член, возвращающая std::ostream&. Вероятно, лучше написать это вообще

template<typename T>
struct lval { T t; T &getlval() { return t; } };

f(static_cast<std::ostringstream&>(
  lval<std::ostringstream>().getlval() << "Value: " << 5).str().c_str());

Причина, по которой он ничего не принимает void*, заключается в том, что operator<< является функцией-членом. operator<<, который принимает char const*, не является.

Ответ 2

Временное не может быть передано как неконстантная ссылка на функцию, поэтому он не находит правильного оператора потоковой передачи и вместо этого берет тот, у которого есть аргумент void * (он является функцией-членом и, таким образом, называет его временным ОК).

Что происходит с тем, чтобы обойти ограничения при кастинге, я чувствую, что это на самом деле UB, но я не могу сказать точно. Кто-то, безусловно, укажет стандарт.

Ответ 3

Это можно сделать с помощью лямбда-функции С++ 11.

#include <iostream>
#include <sstream>

void f(const char * str)
{
    std::cout << str << std::endl;
}

std::string str(void (*populate)(std::ostream &))
{
    std::ostringstream stream;
    populate(stream);
    return stream.str();
}

int main(int argc, char * * args)
{
    f(str([](std::ostream & ss){ ss << "Value: " << 5;  }).c_str());
    return 0;
}

// g++ -std=c++11 main.cpp -o main
// ./main
// Value: 5

Ответ 4

Если вам нравятся однострочные высказывания, вы можете написать:

// void f(const char* str); 
f(static_cast<ostringstream*>(&(ostringstream() << "Value: " << 5))->str());

Однако вам лучше будет поддерживать код следующим образом:

template <typename V>
  string NumberValue(V val)
  {
     ostringstream ss;
     ss << "Value: " << val;
     return ss.str();
  }
f(NumberValue(5));

Ответ 5

Я использую что-то вроде этого для ведения журнала.

#include <sstream>

using namespace std;

const char *log_text(ostringstream &os, ostream &theSame)
{
  static string ret; // Static so it persists after the call
  ret = os.str();
  os.str(""); // Truncate so I can re-use the stream
  return ret.c_str();
}


int main(int argc, char **argv)
{
  ostringstream  ss;
  cout << log_text(ss, ss << "My first message") << endl;
  cout << log_text(ss, ss << "Another message") << endl;
}

Выход:

Мое первое сообщение

Другое сообщение