En C ++, nous avons des fonctions comme std :: fill
ou std :: fill_n
qui sont des moyens pratiques à une ligne de remplir des tableaux de pointeurs, des vecteurs, std :: array
et autres conteneurs avec des valeurs. Certains conteneurs ont également leurs propres méthodes fill
pour permettre le remplissage avec une valeur constante. Il existe également les fonctions std :: generate {_n}
et std :: iota
, dont la première permet de remplir les éléments à l'aide d'une fonction générateur et la seconde remplit
Ce que je recherche, c'est une solution similaire - de préférence une solution à une ligne et définie dans la bibliothèque standard - qui permet de remplir le conteneur avec une fonction de l'index. Par exemple, ce serait une solution pour un tableau:
std::array<int, 100> arr; for (std::size_t i = 0; i < 100; i++) arr[i] = f(i);
où f (std :: size_t i)
est une fonction de l'index.
Existe-t-il une manière intégrée de faire cela?
5 Réponses :
Vous pouvez utiliser un lambda avec état:
std::array<int, 100> arr; std::generate(arr.begin(), arr.end(), [i = std::size_t(0)]() mutable {return f(i++);});
Mais je pense que cela rend le code plus complexe qu'il ne devrait l'être. Utiliser une boucle simple peut être la meilleure option.
Si vous parcourez toujours tout le tableau, vous n'avez même pas besoin d'utiliser de fonction stl, utilisez simplement la boucle de plage:
#include <algorithm> std::array<int, 100> arr; int idx = 0; std::for_each(begin(arr), end(arr), [&idx](int &n){ n = f(idx++); });
Une autre option consiste à utiliser for_each (cela peut être plus utile, si à l'avenir vous voulez remplir votre tableau seulement partiellement )
std::array<int, 100> arr; int idx = 0; for (auto& item : array) item = f(idx++);
Malheureusement, dans les deux cas , vous devez avoir une variable distincte pour l'index (ici: idx
)
Pensez à écrire votre propre modèle de fonction de type STL , apply_idx_func ()
, pour cela:
100 101 102 103 104 105 106 107 108 109
À titre d'exemple:
auto main() -> int { std::array<int, 10> arr; // just adds 100 to the index auto func = [](size_t idx) -> int { return 100 + idx; }; apply_idx_func(std::begin(arr), std::end(arr), func); for (auto elem: arr) std::cout << elem << ' '; std::cout << '\n'; }
Le résultat est:
template<typename FwdItor, typename F> void apply_idx_func(FwdItor first, FwdItor last, F f) { for (size_t idx = 0; first != last; ++first, ++idx) *first = f(idx); }
Y a-t-il une recommandation de «où» pour mettre l'extension personnalisée? Par exemple quel espace de noms?
@HerpDerpington, vous pouvez créer votre propre espace de noms, par exemple: namespace xtd {...}
( xtd signifierait eXtended sTandarD , contrairement à l'espace de noms std
).
std::transform( arr.begin(), arr.end(), boost::irange(0,arr.size()), [](const auto& val,const auto& index){return f(index);} );
Je n'ai pas pu essayer cela avec un compilateur car je ne pouvais pas en trouver un avec des plages C ++ 20, mais quelque chose comme ça devrait fonctionner une fois que les compilateurs les ont implémentés:
auto const values = std::views::iota{1, arr.size()+1} | std::views::transform(f);
ou, si vous voulez juste la séquence de valeurs générées, créez simplement une plage de celles-ci avec
std::ranges::transform(std::views::iota{1, arr.size()+1}, arr, f);
Jusqu'à ce que vous ayez un compilateur C ++ 20, vous pouvez utiliser l'une des bibliothèques de plages , par exemple dans Boost ou Gamme Eric Nieblers-v3
Si la destination doit vraiment être un std :: array
, une étape manque cependant: convertir la vue en tableau.
même pas gcc.godbolt.org ne semble le savoir, c'est aussi nouveau :( gcc.godbolt.org/ z / pgh7uS
pourquoi n'utilisez-vous pas std :: generate_n? c'est exactement ce que tu veux
c'est ce que tu veux? wandbox.org/permlink/tAwaouhYJtacJNjt
@Oblivion Ce serait une solution, cependant, je recherche quelque chose de plus générique qui ne repose pas sur des variables statiques. Par exemple, une fonction qui accepte une expression lambda.