Я столкнулся с этой проблемой при очистке макросов отладки старого приложения 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";
}
Это работает так, потому что Tracer()
является временным (rvalue), которое не может связываться с неконстантной ссылкой в operator<<(basic_ostream<...>&,
,
Тем не менее, вы можете вызывать функции-члены, такие как operator<<(void const*)
потому что это не требует lvalue.
Затем функция-член возвращает ссылку на объект потока, который Можно использоваться в вызове следующего operator<<
в последовательности.
Вызов любой функции-члена таким образом, как Tracer() << left
или же Tracer() << flush
и, таким образом, «преобразовать» ссылку в ссылку на lvalue вполне безопасно.
Если у вас есть компилятор, совместимый с C ++ 11, стандартная библиотека даже содержит operator<<(basic_ostream<...>&&,
который делает это для вас. В этом случае вам больше не нужен обходной путь.
Прежде всего обратите внимание, что 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