Нет единого и правильного способа включения
std::vector<int> const squared =
convenient::transform(v1, [](int x) { return x*x; });
без потенциальной потери производительности.Вам либо нужен явный
std::vector<int> const squared =
convenient::transform<std::vector> (v1, [](int x) { return x*x; });
Обратите внимание на явное упоминание типа контейнера: итераторы ничего не говорят о контейнере, которому они принадлежат.Это становится очевидным, если вы напоминаете, что итератор контейнера по стандарту разрешен как обычный указатель.
Разрешение алгоритму брать контейнер вместо итераторов также не является решением.Таким образом, алгоритм не может знать, как правильно получить первый и последний элемент.Например, массив long int
не имеет методов для begin()
, end()
и length()
, не во всех контейнерах есть итераторы произвольного доступа, не определены operator[]
.Таким образом, нет действительно общего способа брать контейнеры.
Еще одна возможность, которая допускает независимые от контейнера алгоритмы возврата контейнеров, - это некая универсальная фабрика (см. В прямом эфире на http://ideone.com/7d4E2):
// (not production code; is even lacking allocator-types)
//-- Generic factory. -------------------------------------------
#include <list>
template <typename ElemT, typename CacheT=std::list<ElemT> >
struct ContCreator {
CacheT cache; // <-- Temporary storage.
// Conversion to target container type.
template <typename ContT>
operator ContT () const {
// can't even move ...
return ContT (cache.begin(), cache.end());
}
};
Не так много магии, кромешаблонный оператор приведения. Затем вы возвращаете эту вещь из своего алгоритма:
//-- A generic algorithm, like std::transform :) ----------------
ContCreator<int> some_ints () {
ContCreator<int> cc;
for (int i=0; i<16; ++i) {
cc.cache.push_back (i*4);
}
return cc;
}
и, наконец, используете это так, чтобы написать магический код:
//-- Example. ---------------------------------------------------
#include <vector>
#include <iostream>
int main () {
typedef std::vector<int>::iterator Iter;
std::vector<int> vec = some_ints();
for (Iter it=vec.begin(), end=vec.end(); it!=end; ++it) {
std::cout << *it << '\n';
}
}
Как видите, в operator T
есть копия диапазона.
Перемещение может быть возможно с помощью специализации шаблона в случае, если целевой и исходный контейнеры имеют одинаковый тип.
Редактировать: Как указывает Дэвид, вы можетеКонечно, выполняйте реальную работу внутри оператора преобразования, что, вероятно, не потребует дополнительных затрат (если немного больше работы, то это можно сделать более удобно; это только для демонстрации):
#include <list>
template <typename ElemT, typename Iterator>
struct Impl {
Impl(Iterator it, Iterator end) : it(it), end(end) {}
Iterator it, end;
// "Conversion" + Work.
template <typename ContT>
operator ContT () {
ContT ret;
for ( ; it != end; ++it) {
ret.push_back (*it * 4);
}
return ret;
}
};
template <typename Iterator>
Impl<int,Iterator> foo (Iterator begin, Iterator end) {
return Impl<int,Iterator>(begin, end);
}
#include <vector>
#include <iostream>
int main () {
typedef std::vector<int>::iterator Iter;
const int ints [] = {1,2,4,8};
std::vector<int> vec = foo (ints, ints + sizeof(ints) / sizeof(int));
for (Iter it=vec.begin(), end=vec.end(); it!=end; ++it) {
std::cout << *it << '\n';
}
}
Одно требованиечто у цели есть метод push_back
. Использование std::distance
для резервирования размера может привести к неоптимальной производительности, если целевой итератор-контейнер не является случайным доступом.