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 :
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'; }
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.
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);
Voir aussi Comment construire un std :: array avec une séquence d'index?