Очевидным решением является использование dynamic_cast
в foo
.Но данный код все равно не будет работать.(Ваш пример скомпилируется, но он не будет делать то, что, как вы думаете, должен.) Выражение std::ostringstream()
является временным, вы не можете инициализировать неконстантную ссылку с временным, а первый аргумент std::operator<<( std::ostream&, char const*)
это неконстантная ссылка.(Вы можете вызывать функцию-член временно. Например, std::ostream::operator<<( void const* )
. Таким образом, код будет компилироваться, но он не будет выполнять то, что вы ожидаете.
Вы можете обойти эту проблему, используя что-то вроде:
foo( std::ostringstream().flush() << "number = " << 500 );
std::ostream::flush()
возвращает неконстантную ссылку, поэтому дальнейших проблем не возникает. А на вновь созданном потоке это не работает. Тем не менее, я думаю, вы согласитесь, что это не так.Это не самое элегантное или интуитивно понятное решение.
В таких случаях я обычно создаю класс-оболочку, который содержит собственный std::ostringstream
и предоставляет шаблонный член operator<<
который перенаправляет к содержавшемуся std::ostringstream
. Ваша функция foo
будет принимать ссылку const
на это - или то, что я хочу сделать, это напрямую вызвать деструктор foo
, так что клиентскому коду даже не придетсябеспокоиться об этом, она делает что-то вроде:
log() << "number = " << 500;
Функция log()
возвращает экземпляр класса-оболочки (но см. ниже), а деструктор (последний) этого класса вызывает вашу функцию foo
.
Естьодна небольшая проблема с этим.Возвращаемое значение может быть скопировано и уничтожено сразу после копирования.Что разрушит то, что я только что объяснил;фактически, поскольку std::ostringstream
не копируется, он даже не будет компилироваться.Решение здесь состоит в том, чтобы поместить всю действующую логику, включая экземпляр std::ostringstream
и логику деструктора, вызывающую foo
, в отдельный класс реализации, иметь общедоступную оболочку, имеющую boost::shared_ptr
, и пересылать.Или просто переопределите немного логики общего указателя в вашем классе:
class LogWrapper
{
std::ostringstream* collector;
int* useCount;
public:
LogWrapper()
: collector(new std::ostringstream)
, useCount(new int(1))
{
}
~LogWrapper()
{
-- *useCount;
if ( *useCount == 0 ) {
foo( collector->str() );
delete collector;
delete useCount;
}
}
template<typename T>
LogWrapper& operator<<( T const& value )
{
(*collector) << value;
return *this;
}
};
Обратите внимание, что это легко расширить для поддержки необязательного ведения журнала;просто предоставьте конструктор для LogWrapper, который устанавливает collector
в NULL
, и проверьте это в operator<<
.
РЕДАКТИРОВАНИЕ:
Мне приходит на ум еще одна вещь: вы 'Возможно, вы захотите проверить, вызывается ли деструктор в результате исключения, и не вызывать foo
в этом случае.Логически, я надеюсь, что единственное исключение, которое вы можете получить, это std::bad_alloc
, но всегда найдется пользователь, который напишет что-то вроде:
log() << a + b;
, где +
- это определенная пользователем перегрузка, котораяброски.