Применить бинарный оператор к каждому элементу в массиве - PullRequest
1 голос
/ 05 мая 2019

Учитывая бинарный объект функтора (или подобный функтору), который принимает два массива типа T* и размер и возвращает T*, каков канонический способ применить этот функтор к каждому объекту в массиве, возвращая новый?

Т.е., что-то эквивалентное, но более короткое и менее подверженное ошибкам, чем следующее:

template<typename T, typename O>
T* apply(const T* a1, const T* a2, size_t size, O op) {
  T* out = new[size];
  for (size_t i = 0; i < size; i++) {
    out[i] = op(a1[i], a2[i]);
  }
  return out;
}

Я не слишком беспокоюсь о деталях распределения выходных данных. В приведенном выше примере он размещен в куче, но, возможно, другие варианты перезаписывают один из входных данных или записывают в предоставленный буфер, или, возможно, он использует std::array<> и возвращает его и т. Д.

1 Ответ

2 голосов
/ 05 мая 2019

Вы упоминаете о работе с массивами, но с функцией, которая использует итераторы / указатели, где мы потеряли информацию о типе из массива (ов), аналогично распаду указателя массива на указатель при обработке в стиле C в стиле C аргументы массива.

Если применимо (учитывая ограничения с сайта вызова), вместо этого можно использовать std::array, чтобы избежать потери информации о размере, при этом все еще писать шаблон функции, не зависящий от размера, с ограничением, что оба аргумента массива должны иметь тот же самый размер (который мы могли бы ожидать, когда вы выполняете обычную операцию zip и map).

например. используя std::transform, чтобы применить двоичный оператор к попарным элементам в аргументах массива:

#include <algorithm>
#include <array>
#include <iostream>

template <typename T, typename BinaryOperator, std::size_t N>
auto apply(const std::array<T, N>& arr1, const std::array<T, N>& arr2,
           const BinaryOperator& op) {
  std::array<T, N> result{};
  std::transform(arr1.begin(), arr1.end(), arr2.begin(), result.begin(), op);
  return result;
}

int main() {
  const std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
  const std::array<int, 5> arr2 = {6, 7, 8, 9, 10};
  const auto results = apply(arr1, arr2, [](int i, int j) { return i + j; });
  for (const auto num : results) {
    std::cout << num << " ";
  }  // 7 9 11 13 15
}

Или, если вы можете использовать C ++ 14, std::integer_sequence в сочетании с расширением пакета параметров:

#include <array>
#include <iostream>
#include <utility>

namespace detail {
template <typename Array, typename BinaryOperator, std::size_t... I>
Array apply_impl(const Array& arr1, const Array& arr2, const BinaryOperator& op,
                 std::index_sequence<I...>) {
  return {op(arr1[I], arr2[I])...};
}
}

template <typename T, typename BinaryOperator, std::size_t N,
          typename Indices = std::make_index_sequence<N>>
auto apply(const std::array<T, N>& arr1, const std::array<T, N>& arr2,
           const BinaryOperator& op) {
  return detail::apply_impl(arr1, arr2, op, Indices{});
}

int main() {
  const std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
  const std::array<int, 5> arr2 = {6, 7, 8, 9, 10};
  const auto results = apply(arr1, arr2, [](int i, int j) { return i + j; });
  for (const auto num : results) {
    std::cout << num << " ";
  }  // 7 9 11 13 15
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...