ostrstream интерпретирует константу как указатель

Я столкнулся с этой проблемой при очистке макросов отладки старого приложения C / C ++: у нас есть класс Tracer, унаследованный от ostrstream (Я знаю, что это устарело с C ++ 98, но это приложение было написано в 1998 году!), Которое мы используем так:

Tracer() << "some" << " message" << " here";

Теперь, если первое значение в цепочке является константной строкой, как указано выше, результат вызова ostrstream::str() В Tracer (что делается в деструкторе, вставляя результат в очередь), содержится шестнадцатеричное представление указателя на эту строку вместо текста. Таким образом, приведенное выше утверждение приведет к "0x401a37 message here", Это не происходило со старыми макросами, так как они всегда имели long (Thread ID) в качестве первого значения, которое теперь было удалено.

Переход на него с помощью gdb показал, что для первой вставки это вызывает operator<<(void const*) на ostrstream, в то время как последующие вставки вызывают operator<< <...>(basic_ostream<...>&, char const*) (удалены шаблоны для удобства чтения).

Может кто-нибудь объяснить это поведение? Какой будет чистый способ исправить это? Я нашел легкий обходной путь, который использует << left в качестве первого аргумента — это безопасно? Есть ли лучшие способы сделать это?

Вот минимизированный пример:

#include <strstream>
#include <iostream>

using namespace std;

class Trace : public ostrstream {
public:
Trace();
virtual ~Trace();
};

Trace::Trace() : ostrstream() {}
Trace::~Trace() {
static_cast< ostrstream& >(*this) <<ends;
char * text = ostrstream::str();
cout << "MESSAGE: "<< text <<endl;
delete[] text;
}

int main(){
Trace() << "some" << " text" << " here";
Trace() << left << "some" << " text" << " here";
Trace() << 123 << " text" << " here";
}

3

Решение

Это работает так, потому что Tracer() является временным (rvalue), которое не может связываться с неконстантной ссылкой в operator<<(basic_ostream<...>&, ,

Тем не менее, вы можете вызывать функции-члены, такие как operator<<(void const*)потому что это не требует lvalue.

Затем функция-член возвращает ссылку на объект потока, который Можно использоваться в вызове следующего operator<< в последовательности.

Вызов любой функции-члена таким образом, как Tracer() << left или же Tracer() << flushи, таким образом, «преобразовать» ссылку в ссылку на lvalue вполне безопасно.

Если у вас есть компилятор, совместимый с C ++ 11, стандартная библиотека даже содержит operator<<(basic_ostream<...>&&, который делает это для вас. В этом случае вам больше не нужен обходной путь.

5

Другие решения

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

В вашем коде выражение Trace() << "xyz" можно вызывать только функции-члены, потому что Trace() создает временную область, которая не может быть привязана к первому параметру не-члена operator<< функции, так как эти функции принимают первый аргумент как std::ostream& это неконстантная ссылка. Так Trace() << "xyz" решает в член operator<< который занимает void* в качестве аргумента, который печатает адрес!


Мои советы:

  • Не наследовать от класса потока (std::ostrstream является осуждается тем не мение).
  • Скорее напишите простую обертку над потоком класса и перегрузки operator<<

Вот один пример:

#include <sstream> //for std::ostringstream

struct Trace
{
std::ostringstream ss;

template<typename T>
Trace& operator << (T const & data)
{
ss << data;
return *this;
}
~Trace()
{
std::cout << ss.str() << std::endl;
}
};

Теперь вы можете использовать его как:

Trace() << "Hello World\n" << 100 << "\nBye\n";

Выход:

Hello World
100
Bye

Live Demo

5

По вопросам рекламы [email protected]