3
votes

Comment remplir std :: array avec range from range-v3?

Je suis un débutant complet avec la bibliothèque range-v3 .. Supposons que je veuille remplir un std :: array avec des nombres aléatoires dans un certain intervalle.

Avec les itérateurs, je ferais quelque chose comme cette réponse , en passant des itérateurs à mon std :: array en question comme arguments.

auto random_num() -> int {
  static std::mt19937 engine{std::random_device{}()};
  static std::uniform_int_distribution<int> dist(1, 10);
  return dist(engine);
}

std::vector<int> nums = ranges::view::generate_n(random_num, 10);

Avec la bibliothèque de gammes, j'ai voulu utiliser ranges :: view :: generate_n , avec une fonction unaire qui génère un seul nombre aléatoire le long avec la taille de mon tableau.

template< class Iter >
void fill_with_random_int_values( Iter start, Iter end, int min, int max)
{
    static std::random_device rd;    // you only need to initialize it once
    static std::mt19937 mte(rd());   // this is a relative big object to create

    std::uniform_int_distribution<int> dist(min, max);

    std::generate(start, end, [&] () { return dist(mte); });
}

Cela fonctionne bien pour un std :: vector , mais je suis plutôt perdu sur quel algorithme je devrais être utiliser pour remplir un std :: array plutôt que de générer un std :: vector , car une approche similaire à ci-dessus ne fonctionne pas. Je peux transformer le tableau et ignorer chaque argument, mais cela ne semble pas correct.


3 Réponses :


4
votes

La taille d'un std :: array est une constante de compilation, donc je ne vois pas comment la bibliothèque peut en générer une pour vous en utilisant un argument d'exécution.

Voici un élément assez trivial mise en œuvre qui, je pense, fait ce dont vous avez besoin:

#include <random>
#include <array>
#include <utility>
#include <iostream>
#include <boost/range.hpp>

template<std::size_t N>
constexpr auto c_size_t = std::integral_constant<std::size_t, N>();

auto random_num() -> int {
  static std::mt19937 engine{std::random_device{}()};
  static std::uniform_int_distribution<int> dist(1, 10);
  return dist(engine);
}

template<class F, std::size_t...Is>
constexpr auto generate_array_impl(F&& f, std::index_sequence<Is...>) -> std::array<decltype(f()), sizeof...(Is)>
{
    return std::array<decltype(f()), sizeof...(Is)>
    {{
        (void(Is), f())...
    }};
}

template<std::size_t N, class F>
constexpr auto generate_array(F&& f, std::integral_constant<std::size_t, N>) -> std::array<decltype(f()), N>
{
    return generate_array_impl(f, std::make_index_sequence<N>());
}

int main()
{
    auto arr = generate_array(random_num, c_size_t<10>);
    for (auto x : arr)
        std::cout << x << ',';
    std::cout << '\n';

    constexpr auto arr2 = generate_array([i = std::size_t(0)]() mutable { return i++; }, c_size_t<10>);
    for (auto x : arr2)
        std::cout << x << ',';
    std::cout << '\n';
}

https://coliru.stacked-crooked.com/a/983064b89c4dd355

Ou ajouter de la magie constexpr ...

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

auto random_num() -> int {
  static std::mt19937 engine{std::random_device{}()};
  static std::uniform_int_distribution<int> dist(1, 10);
  return dist(engine);
}

template<class F, std::size_t...Is>
auto generate_array_impl(F&& f, std::index_sequence<Is...>) -> std::array<decltype(f()), sizeof...(Is)>
{
    return std::array<decltype(f()), sizeof...(Is)>
    {{
        (void(Is), f())...
    }};
}

template<std::size_t N, class F>
auto generate_array(F f) -> std::array<decltype(f()), N>
{
    return generate_array_impl(f, std::make_index_sequence<N>());
}


int main()
{
    auto arr = generate_array<10>(random_num);
    for (auto x : arr)
    std::cout << x << '\n';
}

https://coliru.stacked-crooked.com/a/42c9c011026779eb p>


0 commentaires

4
votes

std :: array est un agrégat; il n'a aucun constructeur fourni par l'utilisateur. En tant que tel, il n'a pas de constructeurs qui créent l'objet à partir d'une plage.

Vous ne pouvez pas non plus (en C ++ 17) écrire une fonction qui prend une plage et renvoie un tableau. La raison étant que les paramètres ne sont pas constexpr et que la taille d'un tableau doit être une expression constante. C ++ 20 semble ajouter la possibilité de prendre plus de types comme paramètres de modèle non-type, il devrait donc être possible de le faire en tant que paramètre de modèle. Le code ressemblerait à:

template<auto rng>
    requires std::ranges::SizedRange<decltype(rng)>
constexpr auto array_from_range()
{
  std::array<std::iter_value_t<rng>, std::ranges::size(rng)> ret;
  std::ranges::copy(rng, ret);
  return ret;
}

Bien sûr, cela nécessite que la plage elle-même soit constexpr , pas seulement sa taille.


0 commentaires

1
votes

Puisque la taille du tableau est une constante à la compilation. Vous devez le construire vous-même. Vous pouvez le remplir ensuite comme

std::array<int, 10> arr;
ranges::generate(arr, random_num);


0 commentaires