Установить массив в стиле C с функцией шаблона, принимающей обратный вызов - PullRequest
0 голосов
/ 29 августа 2018

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

Самым чистым способом, который я могу найти, является следующее:

#include <functional>

template<typename T, typename Y>
void PopulateArray(std::function<int(Y*)> callback, T &pArray)
{
  for (int i = 0; i < sizeof(pArray); ++i)
    int x = callback(&pArray[i]);
}

int main()
{
  uint64_t myArray[5];
  uint64_t myUint = 42;
  PopulateArray( (std::function<int(uint64_t*)>) [=](auto x) {*x = myUint; return 0; },
    myArray);
}

У меня есть две проблемы с кодом выше.

1) Для T, чтобы быть типом массива, кажется, нет никакого способа изменить параметр. (Я не могу сказать, что мне нужен массив типа T, то есть я должен объявить Y отдельно, даже если они оба связаны с uint64_t.) Я бы предпочел объявить single T, один из которых является указателем на T, а другой - массивом T.

2) Код клиента (в основном) принудительно приводит к лямбде. Изменение auto x на явный тип, похоже, не помогает.

Существует ли разрешение на № 1 или № 2, которое может сделать код более лаконичным или читабельным?

Код необходимо будет компилировать с помощью gcc, clang и VS. Я думаю, что C ++ 11 - это новейший стандарт, который я могу использовать, хотя я был бы заинтересован в решениях C ++ 14, так как это было бы вопросом обновления нашего процесса сборки clang. Меня не интересуют решения, включающие переключение myArray на std::array std::vector и т. Д.

1 Ответ

0 голосов
/ 29 августа 2018

Откажитесь от требования std::function:

// You could consider using an array type for the parameter:
// template <typename Callback, typename T, std::size_t N>
// void PopulateArray(Callback callback, T (&pArray)[N])
template<typename Callback, typename T>
void PopulateArray(Callback callback, T& pArray)
{
  // sizeof(pArray) as in the question is almost certainly not what you 
  // want. It returns the size *in bytes*, not the length of the array.
  // Thus, if you specified this to take an array reference,
  // `for (std::size_t i = 0; i < N; ++i)` would be correct.

  // However, as Barry mentioned in the comments, a range-based for loop
  // is the best solution.
  for (T& element : pArray)
    callback(&element);
}

int main()
{
  std::uint64_t myArray[5];
  PopulateArray([](auto x) {*x = 42; return 0; },
    myArray);
}

std::function - это дорогой тип. Он использует виртуальные вызовы функций (или методы, которые очень похожи) и имеет потенциал для выделения памяти. Если вы не сохраняете функцию и особенно если функция уже является шаблоном, просто возьмите произвольный обратный вызов в качестве параметра. Если вы действительно хотите ограничить тип обратного вызова, используйте тип function_ref (еще не стандартизированный) или убедитесь, что callback(your, args) действительно:

template<typename Callback, typename T>
auto PopulateArray(Callback callback, T& pArray)
    -> decltype(callback(*std::begin(pArray)), void())
{
  for (T& element : pArray)
    callback(&element);
}

Кроме того, в этом конкретном случае вы можете использовать алгоритм :

int main()
{
  uint64_t myArray[5];
  uint64_t myUint = 42;
  // If it's all the same value:
  std::fill(std::begin(myArray), std::end(myArray), myUint);
  // To call a function to populate the array:
  std::generate(std::begin(myArray), std::end(myArray), [myUint] {
    return myUint;
  });
  // Or possibly:
  std::for_each(std::begin(myArray), std::end(myArray),
    [myUint](uint64_t& element) {
      element = myUint;
    });
}
...