зачем нужна перегрузка при использовании оператора ‹< через шаблон?

Как и в этом вопросе, я экспериментирую с потоковой передачей через класс, использующий SBRM / RAII, поэтому

SBRM(x) << "test";

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

Что у меня (упрощено для ясности):

#include <iostream>
#include <sstream>

class SBRM
{
public:
  SBRM(int j) : i(j) {}
  ~SBRM() { std::cout << "SBRM(" << i << "): " << oss.str() << std::endl; }

  template<typename T> SBRM& operator<<(T& in) { oss << in; return *this; }
  // SBRM& operator<<(const long long& in) { oss << std::hex << "0x" << in; return *this; }
  SBRM& operator<<(const double& in) { oss << in; return *this; }
  SBRM& operator<<(const void* in) { oss << in; return *this; }

private:
  int i;
  std::ostringstream oss;
};


int main()
{
  std::string ttt = "world";
  const int i = 3;
  SBRM(1) << "Hello";
  SBRM(2) << ttt;
  SBRM(3) << 0x1234567890123ll; 
  SBRM(4) << &i;
  SBRM(5) << 5;
  SBRM(6) << 0.23;
  SBRM(7) << i;
  SBRM(8) << 5 << ", " << ttt << ", " << &i;
}

Такого рода работы:

SBRM(1): Hello
SBRM(2): world
SBRM(3): 3.20256e+14
SBRM(4): 0xbf8ee444
SBRM(5): 5
SBRM(6): 0.23
SBRM(7): 3
SBRM(8): 5, world, 0xbf8ee444

но меня больше всего беспокоит: почему компилятор требует от меня перегрузки шаблона при использовании (не строковых) литералов?
Есть ли какие-нибудь уловки, чтобы этого избежать, или я использую неправильный подход? Другие предложения приветствуются, потому что теперь я прибег к использованию макроса для

NOT_QUITE_SBRM_MACRO(3, "At least, " << 5 << ", this works");

Проблема наблюдается в gcc 4.1.2. и 4.4.3. Без перегруженных функций я получаю:

sbrm-stream.cpp: In function ‘int main()’:
sbrm-stream.cpp:27: error: no match for ‘operator<<’ in ‘SBRM(3) << 320255973458211ll’
sbrm-stream.cpp:10: note: candidates are: SBRM& SBRM::operator<<(T&) [with T = long long int]
sbrm-stream.cpp:28: error: no match for ‘operator<<’ in ‘SBRM(4) << & i’
sbrm-stream.cpp:10: note: candidates are: SBRM& SBRM::operator<<(T&) [with T = const int*]
...

person stefaanv    schedule 05.11.2010    source источник


Ответы (1)


Поскольку вы ожидаете аргумент не const, а литералы никогда не могут рассматриваться как таковые. Аргументируйте const, и ваши проблемы исчезнут:

template<typename T> SBRM& operator<<(T const& in) { oss << in; return *this; }

И как Дэвид упомянул в своем комментарии, вам нужны перегрузки при использовании таких манипуляторов, как endl. Вот выстрел в них:

SBRM& operator <<(std::ostream& (*manip)(std::ostream&)) {
    oss << manip; // alternatively: manip(os);
    return *this;
}

// same for:

ostream& operator <<(ios& (*manip)(ios&));
ostream& operator <<(ios_base& (*manip)(ios_base&));

Это охватывает все манипуляторы без параметров.

Я не совсем понимаю, как работают параметризованные манипуляторы из <iomanip>, но похоже, что они возвращают прокси-объект, который может использовать общий вариант operator <<.

person Konrad Rudolph    schedule 05.11.2010
comment
@stefaanv: всегда намного сложнее выявлять проблемы в самом разгаре :) Кстати, при передаче по ссылке / указателю всегда старайтесь использовать const и удаляйте его только тогда, когда вам действительно нужно. Здесь также можно использовать typename boost::call_traits<T>::param_type для передачи либо по значению, либо по ссылке в зависимости от того, что наиболее эффективно (т. Е. Встроенное получение по значению). Проверьте: boost.org/doc/libs/1_44_0/libs /utility/call_traits.htm - person Matthieu M.; 05.11.2010
comment
Вам все равно потребуется предоставлять перегрузки, если вы хотите обрабатывать манипуляторы, но это добавляет только 3 перегрузки с указателями функций (возможно, их можно объединить в один шаблон, но я не уверен в этом) - person David Rodríguez - dribeas; 05.11.2010
comment
+1 за этот ответ. Это похоже на вопрос, который был вызван неработающей страницей cplusplus.com о оператор ‹< - person Johannes Schaub - litb; 06.11.2010