Странное поведение с ostringstream

#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: Интересно. Можете ли вы опубликовать это? Макрос и то, как вы его используете, оба!