2013-02-12 1 views
5

В настоящее время я пишу обертку для std::stringstream, и я хочу переслать все вызовы operator<< через мой класс в . Это отлично работает сейчас (спасибо за этот вопрос: wrapper class for STL stream: forward operator<< calls), но есть еще одна проблема с ним.Класс оболочки C++ для iostream, используйте модификаторы потока, такие как std :: endl с оператором <<

Скажем, у меня есть следующий код:

class StreamWrapper { 
private: 
    std::stringstream buffer; 
public: 
    template<typename T> 
    void write(T &t); 

    template<typename T> 
    friend StreamWrapper& operator<<(StreamWrapper& o, T const& t); 

    // other stuff ... 
}; 


template<typename T> 
StreamWrapper& operator<<(StreamWrapper& o, T const& t) { 
    o.write(t); 
    return o; 
} 

template<typename T> 
void StreamWrapper::write(T& t) { 
    // other stuff ... 

    buffer << t; 

    // other stuff ... 
} 

Если теперь я это сделать:

StreamWrapper wrapper; 
wrapper << "text" << 15 << "stuff"; 

Это работает просто отлично. Но если я хочу использовать модификаторы потока, такие как std::endl, который является функцией в соответствии с http://www.cplusplus.com/reference/ios/endl, я просто не компилирую.

StreamWrapper wrapper; 
wrapper << "text" << 15 << "stuff" << std::endl; 

Почему? Как я могу переслать модификаторы потока тоже?

+0

Что ошибка компиляции? –

+0

Есть перегрузки 'operator <<', которые принимают функцию, а затем вызывают эту функцию в потоке: http://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt http: //en.cppreference .com/w/cpp/io/manip – BoBTFish

ответ

3

См. this answer.

Вы хотите

typedef std::ostream& (*STRFUNC)(std::ostream&); 

StreamWrapper& operator<<(STRFUNC func) // as a member, othewise you need the additional StreamWrappe& argument first 
{ 
    this->write(func); 
    return *this; 
} 
+0

@Mogria почему редактирование? Объекты 'STRFUNC' являются функциями (указатели), поэтому их можно вызвать непосредственно из' ostream'. – rubenvb

+0

, потому что он должен пройти мой метод записи, из-за другого материала в нем. – MarcDefiant

2

Вы, кажется, делает немного дополнительной работы. Я обычно использую:

class StreamWrapper 
{ 
    // ... 
public: 
    template <typename T> 
    StreamWrapper& operator<<(T const& obj) 
    { 
     // ... 
    } 
}; 

С современным компилятором, это должен работа для всех конкретных типов. Проблема в том, что манипуляторы являются функциями шаблона, , поэтому компилятор не может выполнить шаблонный аргумент типа вычет. Решение состоит в том, чтобы обеспечить нешаблонные перегрузки для типов манипуляторов:

StreamWrapper& operator<<(std::ostream& (*pf)(std::ostream&)) 
{ 
    // For manipulators... 
} 

StreamWrapper& operator<<(std::basic_ios<char>& (*pf)(std::basic_ios<char>&) 
{ 
    // For manipulators... 
} 

Тип вычет не получится для манипуляторов, но компилятор подберет их для разрешения перегрузки функции.

(Обратите внимание, что вам может понадобиться еще больше, для таких вещей, как std::setw(int).)

+0

Является ли совершенная идиома переадресации проблемой, если у вас есть эталонная поддержка r-value? –

+0

@ Тит, я так не думаю. Я не понимаю, как это изменит что-нибудь. По словам Скотта Мейера (которому я определенно доверяю), имена шаблонов не могут быть идеально переадресованы (и я не вижу, как вы могли бы реализовать его в компиляторе), а манипуляторы - это все имена шаблонов. –

+0

Если он это сказал, тогда я тоже этому верю. Я только в своей памяти повторял его заявления о том, что универсальные ссылки «привязываются ко всему». –