Как реализовать что-то вроде std :: copy_if, но применить функцию перед вставкой в ​​другой контейнер - PullRequest
0 голосов
/ 22 января 2019

Полное раскрытие, это может быть сложная ситуация, когда пытаются использовать алгоритмы STL, когда они не нужны.Я видел вновь появляющийся образец в некотором коде C ++ 14, с которым я работаю.У нас есть контейнер, который мы перебираем, и если текущий элемент соответствует некоторому условию, мы копируем одно из полей элементов в другой контейнер.

Шаблон выглядит примерно так:

 for (auto it = std::begin(foo); it!=std::end(foo); ++it){
    auto x = it->Some_member;
    // Note, the check usually uses the field would add to the new container. 
    if(f(x) && g(x)){ 
      bar.emplace_back(x);
    }
  }

Идея почти накапливается, когда применяемая функция не всегда возвращает значение.Я могу думать только о решениях, которые либо

  • требуют функции для доступа к элементу, который вы хотите накапливать, и другой функции для проверки состояния.т.е. Как объединить std :: copy_if и std :: transform?
  • Хуже, чем то, что я хочу заменить.

Это даже хорошая идея?

Ответы [ 2 ]

0 голосов
/ 22 января 2019

Довольно общим решением вашей проблемы было бы следующее (рабочий пример):

#include <iostream>
#include <vector>
using namespace std;

template<typename It, typename MemberType, typename Cond, typename Do>
void process_filtered(It begin, It end, MemberType iterator_traits<It>::value_type::*ptr, Cond condition, Do process)
{
    for(It it = begin; it != end; ++it)
    {
        if(condition((*it).*ptr))
        {
            process((*it).*ptr);
        }
    }
}

struct Data
{
    int x;
    int y;
};

int main()
{
    // thanks to iterator_traits, vector could also be an array;
    // kudos to @Yakk-AdamNevraumont
    vector<Data> lines{{1,2},{4,3},{5,6}};

    // filter even numbers from Data::x and output them
    process_filtered(std::begin(lines), std::end(lines), &Data::x, [](int n){return n % 2 == 0;}, [](int n){cout << n;});

    // output is 4, the only x value that is even

    return 0;
}

Это не использует STL, это правильно, но вы просто передаете пару итераторов, член для поискаи две лямбда-функции / функции, которые будут сначала фильтровать, а затем использовать отфильтрованный вывод соответственно.

Мне нравятся ваши общие решения, но здесь вам не нужно иметь лямбду, которая извлекает соответствующий атрибут.

Ясно, что код может быть улучшен для работы с const_iterator, но для общей идеи, я думаю, это должно быть полезно.Вы также можете расширить его, чтобы иметь функцию-член, которая возвращает атрибут-член вместо прямого указателя на атрибут-член, если вы хотите использовать этот метод для инкапсулированных классов.

0 голосов
/ 22 января 2019

Конечно.Существует несколько подходов.

  1. Найти библиотеку с transform_if, например boost.

  2. Найти библиотеку с transform_range, который принимает преобразование и диапазон или контейнер и возвращает диапазон с преобразованным значением.Составьте это с помощью copy_if.

  3. Найдите библиотеку с filter_range, как указано выше.Теперь используйте std::transform с вашим фильтруемым диапазоном.

  4. Найдите один с обоими, и составьте фильтрацию и преобразование в соответствующем порядке.Теперь ваша проблема - просто копирование (std::copy или что-то в этом роде).

  5. Напишите свою собственную оболочку для обратной вставки, которая трансформируется при вставке.Используйте это с std::copy_if.

  6. Напишите свои собственные адаптеры диапазона, как 2 3 и / или 4.

  7. Напишите transform_if.

...