Определение функции, чтобы она могла принимать либо список, либо вектор

#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() , которая принимает пару итераторов вместо контейнера, когда вы не хотите обрабатывать все элементы контейнера, а только поддиапазон элементов в нем.