Передать итератор как параметр функции
Я пытаюсь написать функцию, которая будет суммировать элементы контейнера. Этот контейнер может быть вектором, списком, очередью и т. д... Вот почему я пробовал шаблоны.
к сожалению, я получаю эту ошибку:
'C' не является шаблоном
источник:
#include <iostream>
#include <vector>
using namespace std;
template<class C, typename T>
T sum( C<T>::iterator begin, C<T>::iterator end ) {
T s = null;
for (C<T>::iterator it = begin; it != end; it++) {
s += *it;
}
return s;
}
int main()
{
vector<int> v = {5, 9, 0, 11};
cout << sum(v.begin(), v.end()) << endl;
return 0;
}
что я не так? Как это исправить?
2 ответов
конкретная ошибка, которую вы получаете, потому что вам нужен аргумент шаблона шаблона:
template<template <typename> class C, typename T>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
T sum( C<T>::iterator begin, C<T>::iterator end )
однако стандартные контейнеры обычно имеют более одного аргумента шаблона:
template < class T, class Alloc = allocator<T> > class vector
и это немного нетривиально, чтобы написать такую функцию правильно. Вы можете использовать аргументы шаблона variadic, или вы можете делать, как стандартная библиотека, и специализироваться только столько, сколько вам действительно нужно:
// <algorithm>
namespace std {
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
}
в вашем случае (делая вид, что ваша потребность уже не покрывается стандартной библиотекой алгоритмов):
template <typename Iterator>
auto sum(Iterator begin, Iterator end)
-> decltype(*begin+*begin) // the type of summing two of them
{
if (begin == end) throw std::logic_error("....");
auto s = *begin;
++begin;
for (; begin != end; ++begin) {
s += *begin;
}
return s;
}
есть еще несколько отличий от своего оригинала код:
- новый код не принимает значение null или конструктор по умолчанию (
T s = null;
) - не вводит дополнительный итератор (
it
) - использует pre-increment
- создает исключение, когда begin==end
если вы добавляете init
параметр, вы можете сделать его почти noexcept
:
template <typename Iterator, typename T>
T sum(Iterator begin, Iterator end, T init)
{
for (; begin!=end; ++begin)
init += *begin;
return init;
}
но только почти, потому что init += *begin
все еще может бросить.
если у вас есть такая подпись, Вы, кстати, воспроизвели подпись std::accumulate
.
вы можете выразить все это в терминах типа итератора и использовать iterator_traits
чтобы получить value_type:
#include <iterator>
template<typename Iterator>
typename std::iterator_traits<Iterator>::value_type
sum(Iterator begin, Iterator end)
{
using value_type = typename std::iterator_traits<Iterator>::value_type;
value_type s = value_type();
for (Iterator it = begin; it != end; it++) {
s += *it;
}
return s;
}
В РЕАЛЬНОЙ ЖИЗНИ, использовать С std::накапливать:
int sum = std::accumulate(v.begin(), v.end(), 0);