#c #stl
#c #stl
Вопрос:
У меня есть функция, которая должна получать либо std::list
, либо std::vector
MyClass *
of, и выполнять множество операций обработки в зависимости от того, что внутри. Я не хочу дублировать тело функции.
Мне нужно использовать эти объекты только для перебора по ним и выполнения некоторых проверок только для чтения.
Я думал о передаче .begin()
и .end()
итераторов напрямую, но это совсем не кажется привлекательным.
Есть ли лучший способ обойти это, в настоящее время у меня есть следующее решение (которое создает другой список из передаваемого вектора, что тоже вряд ли идеально).
void process(std::list<MyClass*> input)
{
//A lot of processing
BOOST_FOREACH(MyClass* itMyClass, input)
{
//some checks, creating new list based on the checks
}
//A lot of processing
}
void process(std::vector<MyClass*> input)
{
process(std::list<MyClass*>(input.begin(), input.end()));
}
Редактировать:
Кажется, что многие люди предлагают пойти на begin()
и end()
, в конце концов, я заставил это работать так, как показано в примере ниже. Спасибо за вашу помощь.
//This one is private
template <typename Iterator>
void process(Iterator begin, Iterator end)
{
//A lot of processing
for (; begin != end; begin)
{
//some checks, creating new list based on the checks
}
//A lot of processing
}
void process(std::list<MyClass*> input)
{
process(input.begin(), input.end());
}
void process(std::vector<MyClass*> input)
{
process(input.begin(), input.end());
}
Комментарии:
1. Использовать шаблон?
2. Даже если итераторы выглядят некрасиво, они совместимы со стандартной библиотекой и очень универсальны. Стоит пересмотреть свою позицию в отношении них.
3. Я также голосую за передачу итераторов begin и end напрямую. Это лучший способ отделить ваш алгоритм от контейнеров, которые вы хотите обработать.
STL
Это гениальная работа, даже если она не всегда выглядит красиво.4. В качестве альтернативы, вы могли бы передать начальный и конечный итераторы, а не контейнер. (Что работает, если вы выполняете итерацию, но не так сильно, если вы изменяете контейнер.)
5. Кажется, что begin () и end () — это выбор людей, и это была моя первая мысль тоже. Я полагаю, что у меня будет два общедоступных переопределения, одно для vector, одно для list, и оба передадут итераторы begin() и end () частной функции, которая будет иметь логику. Спасибо
Ответ №1:
Для этого вы можете использовать шаблон функции:
template<class ListOrVector>
void process(ListOrVector constamp; input) {
//your code
}
//You can also use a template template parameter
template<template<class My, class Alloc = std::allocator<My>> class ListOrVector>
void process(ListOrVector<MyClass*, Alloc> constamp; input) { ... }
Обратите внимание, что я беру ListOrVector по ссылке const ( const amp;
). Это предотвратит копирование.
Редактировать
Я исправил второй пример. class
Перед ListOrVector отсутствовал, а распределитель std::allocator<My
используется по умолчанию.
Комментарии:
1. @YuKo Тебя, вероятно, вообще не волнует распределитель. Итак, вы могли бы упростить это до: template<шаблон<класс Мой> class ListOrVector> аннулировать процесс (ListOrVector<MyClass*> const amp; input)
Ответ №2:
Так же, как и обычные функции, шаблоны функций также могут быть перегружены, так что вы можете получить лучшее из обоих миров: алгоритм на основе итератора для большей гибкости и алгоритм на основе контейнера для простоты использования.
Таким образом, вы можете использовать перегрузку на основе итератора для обработки поддиапазона контейнера, а перегрузку на основе контейнера — для обработки всех элементов контейнера.
process(first, last)
Я бы предложил сначала определить шаблон функции, process()
который принимает пару итераторов для последовательности элементов, которые вы хотите обработать:
template<typename Iterator>
void process(Iterator begin, Iterator end) {
for (auto it = begin; it != end; it) {
// ...
}
}
Это алгоритм, основанный на итераторе (т. Е. он использует пару итераторов), и соответствует тому же подходу, которому следуют алгоритмы STL.
process(container)
Затем я бы определил другой шаблон функции, process()
который перегружает первый. Эта перегрузка принимает контейнер и вызывает версию на основе итератора process()
для элементов переданного контейнера:
template<typename Container>
void process(Container constamp; c) {
process(std::begin(c), std::end(c));
}
Это контейнерный алгоритм, поскольку он принимает контейнер.
Таким образом, вы можете использовать алгоритм на основе контейнера вместо алгоритма на основе итератора:
std::vector<int> vec{1, 2, 3};
std::list<int> lst{1, 2, 3};
std::array<int, 3> arr{1, 2, 3};
int carr[] = {1, 2, 3};
process(vec);
process(lst);
process(arr);
process(carr);
Этот алгоритм не связан с контейнерами для обработки (например, он работает даже с массивами в стиле C, как вы можете видеть), но вы вызываете его непосредственно в контейнере вместо передачи итераторов.
Вы всегда можете вызвать перегрузку process()
, которая принимает пару итераторов вместо контейнера, когда вы не хотите обрабатывать все элементы контейнера, а только поддиапазон элементов в нем.