Как я могу распечатать содержимое любого контейнера в общем виде?

Я пытаюсь написать кусок кода для удовольствия, используя шаблоны С++.

#include <iostream>
#include <vector>

template <class Container>
std::ostream& operator<<(std::ostream& o, const Container& container)
{
    typename Container::const_iterator beg = container.begin();

    o << "["; // 1

    while(beg != container.end())
    {
        o << " " << *beg++; // 2
    }

    o << " ]"; // 3

    return o;
}

int main()
{
    std::vector<int> list;

    list.push_back(0);
    list.push_back(0);

    std::cout << list;

    return 0;
}

Приведенный выше код не компилируется :)

На 1, 2, 3 выдается та же ошибка: ошибка C2593: 'оператор ‹‹' неоднозначен

Все, что я пытаюсь сделать, это перегрузить оператор ‹‹ для работы с любым контейнером. Имеет ли это смысл ? Как это сделать? Если возможно, то почему?

РЕДАКТИРОВАТЬ :: Спасибо за исправления :) 'sth' способ - хорошее решение.

Мне просто любопытно, исчезнет ли эта двусмысленность, как объяснил Нил, если мы сможем использовать концепции C++0x?


person AraK    schedule 20.07.2009    source источник
comment
Какие ошибки компиляции вы получаете?   -  person joshdick    schedule 20.07.2009
comment
Я бы рекомендовал сделать аргумент контейнера константной ссылкой и использовать const_iterator. Это позволяет избежать ненужного копирования и позволяет печатать константные контейнеры.   -  person Fred Larson    schedule 20.07.2009
comment
Что касается концепций, комитет по стандартам C++ только что выкинул их в долгую траву — они не будут частью следующего стандарта.   -  person    schedule 20.07.2009
comment
@Neil, у вас есть указатель, где я могу посмотреть последние обновления состояния C ++ 0x?   -  person David Rodríguez - dribeas    schedule 20.07.2009
comment
Мне стыдно признаться, что я получил это informit.com/guides/content .aspx?g=cplusplus&seqNum=441 вне реддита :-(   -  person    schedule 20.07.2009


Ответы (7)


Вы можете ограничить свой оператор‹‹, чтобы он применялся только к шаблонным контейнерам, указав, что параметр шаблона контейнера сам является шаблонным. Поскольку стандартные контейнеры C++ также имеют параметр шаблона распределителя, вы также должны включить его в качестве параметра шаблона контейнера.

template
    < typename T
    , template<typename ELEM, typename ALLOC=std::allocator<ELEM> > class Container
    >
std::ostream& operator<< (std::ostream& o, const Container<T>& container)
{
    typename Container<T>::const_iterator beg = container.begin();

    o << "["; // 1

    while(beg != container.end())
    {
        o << " " << *beg++; // 2
    }

    o << " ]"; // 3

    return o;
}

int main()
{
    std::vector<int> list;

    list.push_back(0);
    list.push_back(0);

    std::cout << list;

    return 0;
}
person jon-hanson    schedule 20.07.2009
comment
Я до сих пор не понимаю :( какая информация отсутствовала для оператора ‹‹. не могли бы вы объяснить немного больше? как заставить его быть контейнером шаблона разрешила неоднозначность оператора ‹‹ . на самом деле я хочу знать, в чем неоднозначность был на первом месте, когда мы использовали os ‹‹ [; или os ‹‹ *beg++; - person cexplorer; 29.07.2015
comment
Определяемый operator<< является шаблоном для любого типа. Это создает двусмысленность, потому что operator<< уже определено для целых чисел, символов, строк и т. д. Таким образом, вызовы o << "[" вызывают ошибку компилятора, потому что он не знает, какой из них вызывается - исходный << (т.е. из стандартной библиотеки) или новый шаблон <<. Ограничивая определение operator<< только для шаблонных классов, мы устраняем двусмысленность. - person jon-hanson; 01.08.2015

Ваш недавно определенный operator<< соответствует не только контейнерам, но и любым другим типам, таким как целые числа и строки. Вот почему компилятор жалуется на двусмысленность, когда ему нужно найти соответствие operator<< для вывода "[".

Один из способов обойти эту проблему — переименовать функцию вывода:

template <class Container>
std::ostream& container_out(std::ostream& o, const Container &container) {
  // ...
}

Затем вы можете добавить простые оболочки, чтобы включить operator<< для всех контейнеров, которые вы хотите распечатать:

template<typename T>
std::ostream& operator<<(std::ostream& o, const std::vector<T> &container) {
  return container_out(o, container);
}

template<typename T>
std::ostream& operator<<(std::ostream& o, const std::map<T> &container) {
  return container_out(o, container);
}
person sth    schedule 20.07.2009
comment
Я не думаю, что эти специализации операторов будут работать, поскольку они не учитывают необязательный параметр шаблона распределителя. - person Evan Teran; 20.07.2009
comment
И имя _out зарезервировано в области пространства имен. - person ; 20.07.2009
comment
@Evan Teran: это будет работать для векторов, которые используют распределитель по умолчанию, для других определение operator<< необходимо расширить дополнительным аргументом. - person sth; 20.07.2009

В чем ошибка? Я видел один, вам нужно имя типа:

typename Container::iterator beg = container.begin();

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

person Matt Price    schedule 20.07.2009
comment
Я не уверен, но что означает имя типа? - person AraK; 20.07.2009

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

  • использовать именованную функцию, а не оператор
  • передать контейнер по константной ссылке (однако это не имеет отношения к проблеме)
person Community    schedule 20.07.2009

Хорошо, теперь ваш шаблон вызывает путаницу. Компилятор не может выбрать между вашим оператором и тем, который вы ожидаете.

person Matt Price    schedule 20.07.2009

Возможно, не такой общий, как то, что вы публикуете (вам нужно передать содержащийся тип), а также оставляет дополнительный разделитель в конце, но вы можете использовать для этого STL:

std::vector<int> v;
v.push_back( 10 );
v.push_back( 20 );

std::cout << "[";
std::copy( v.begin(), v.end(), std::ostream_iterator<int>( std::cout, " " ) );
std::cout << "]";

Выведет:

[10 20 ]

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

person David Rodríguez - dribeas    schedule 20.07.2009

http://blog.csdn.net/cqdjyy01234/article/details/19234329 может быть хорошим решением. Он работает для контейнеров STL и массива в стиле C.

person cqdjyy01234    schedule 16.02.2014