Превращение временного потока строк в c_str () в одном выражении - PullRequest
14 голосов
/ 12 марта 2010

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

void f(const char* str);

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

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

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

1) Безопасен ли этот бросок?

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

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

2) Почему оператор << на временном std :: ostringstream () вызывает оператор ostream? Конечно, у временного есть тип «ostringstream», а не «ostream»? </p>

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

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

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

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


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

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

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

Ответы [ 5 ]

14 голосов
/ 12 марта 2010

Вы не можете преобразовать временный поток в 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*, не является.

3 голосов
/ 12 марта 2010

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

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

1 голос
/ 04 ноября 2017

Это можно сделать с помощью лямбда-функции C ++ 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
0 голосов
/ 08 мая 2012

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

#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;
}

Выход:

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

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

0 голосов
/ 12 марта 2010

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

// 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));
...