#c #ostringstream
#c #ostringstream
Вопрос:
Я пытался придумать умный способ объединить различные вещи в один строковый аргумент для функции без необходимости использовать ostringstream
явно. Я подумал о:
#define OSS(...)
dynamic_cast<std::ostringstream constamp;>(std::ostringstream() << __VA_ARGS__).str()
Однако, учитывая:
void f( string const amp;s ) {
cout << s << endl;
}
int main() {
char const *const s = "hello";
f( OSS( '{' << s << '}' ) );
ostringstream oss;
oss << '{' << s << '}';
cout << oss.str() << endl;
}
он печатает при запуске:
123hello}
{hello}
где 123 — это ASCII-код для }
. Почему использование макроса приводит к ошибкам?
К вашему сведению: в настоящее время я использую g 4.2.1 в Mac OS X как часть Xcode 3.x.
Решение, которое я сейчас использую
class string_builder {
public:
template<typename T>
string_builderamp; operator,( T const amp;t ) {
oss_ << t;
return *this;
}
operator std::string() const {
return oss_.str();
}
private:
std::ostringstream oss_;
};
#define BUILD_STRING(...) (string_builder(), __VA_ARGS__)
using namespace std;
void f( string const amp;s ) {
cout << s << endl;
}
int main() {
char const *const s = "hello";
f( BUILD_STRING( '{', s, '}' ) );
}
Комментарии:
1. Просто любопытно, почему ты пытаешься быть «умным»?
2. Потому что создавать ostringstream явно утомительно.
3. Зачем изобретать велосипед? boost.org/doc/libs/1_46_1/doc/html/boost/algorithm/join.html
4. @phooji: это присоединяется к предопределенной последовательности, а не к встроенному списку «материала».
Ответ №1:
std::ostringstream()
является временным, который, таким образом, может быть привязан только к ссылкам const. Автономный оператор<< (который принимает неконстантные ссылки в качестве первого аргумента) не рассматривается, а рассматривается только членский. Лучшее соответствие в них для символа — преобразование символа в int.
Эти проблемы часто возникают со строковыми литералами, адрес которых затем отображается.
Чтобы решить проблему, хитрость заключается в том, чтобы найти способ преобразовать временное значение в ссылку. Это делают члены operator<<
, но только один для manipulator делает это без побочного эффекта, и только если манипулятор является noop — flush может быть использован. Члены flush и write также являются кандидатами. Так, например
#define OSS(...)
dynamic_cast<std::ostringstream constamp;>(std::ostringstream().flush() << __VA_ARGS__).str()
Комментарии:
1. Использование
.flush()
заставляет это работать, но теперь компилятор предупреждает о возврате ссылки на временный. Есть какой-нибудь способ избавиться от этого?2. @Paul: Смотрите мое второе решение, опубликованное как отдельное решение. Дайте мне знать, если это сработает для вас.
3. » постоянные ссылки » изменяемы!
4. @curiousguy, исправлено. Спасибо. Не стесняйтесь редактировать сами 🙂
Ответ №2:
Лучшее потокобезопасное решение без использования громоздкого макроса.
Исходный вызов функции таков:
f( OSS( '{' << s << '}' ) );
Как насчет того, чтобы вызвать только это:
f(stringbuilder() << '{' << s << '}' );
где stringbuilder
реализовано как:
struct stringbuilder
{
std::ostringstream ss;
template<typename T>
stringbuilder amp; operator << (const T amp;data)
{
ss << data;
return *this;
}
operator string() { return ss.str(); }
};
void f( string const amp;s ) {
cout << s << endl;
}
Тест:
int main() {
char const *const s = "hello";
f(stringbuilder() << '{' << s << '}' );
}
Вывод:
{hello}
Онлайн-демонстрация:http://ideone.com/QHFf4
Комментарии:
1. Да, это работает, но мне непонятно, почему это работает, а прямое использование ostringstream — нет. Почему в этом решении нет такой же проблемы с временным объектом?
2. @Paul: Потому что теперь
operator<<
это функция-член, которая может быть вызвана и для временного объекта (также). Даже в вашем кодеstd::ostringstream() << '{'
вызывается функция-член, и, к сожалению, не существует функции-члена, которая принимаетchar
, поэтому{
преобразуется вint
, чтобы функция-член могла быть вызвана для временного объекта. Однако, когда объект не является временным, вызывается автономная функция, которая оказывается более подходящей.3. На самом деле, если вы измените operator<< на operator, (запятая), а затем повторно введете мой макрос, вы получите решение, которое выглядит как традиционный вызов функции. Кроме того, это позволяет передавать несколько компоновщиков одной и той же функции в качестве отдельных аргументов.
4. @Paul: В решении для макроса вы не можете заменить
<<
на запятую. Это не сработало бы. Тем не менее, вы можете сделать это в моем решении, но это было бы нехорошо.5. @Paul: Интересно. Можете ли вы опубликовать это? Макрос и то, как вы его используете, оба!