1
votes

Comment initialiser un champ std :: array à l'intérieur d'une structure avec le contenu d'un conteneur de séquence de longueur variable?

J'ai une structure qui, dans une représentation extrêmement simplifiée, ressemble à ceci:

struct Flags {
    const std::array<unsigned int, 8> flags;

    Flags(std::vector<unsigned int> initialFlagValues) : flags(initialFlagValues) {}
};

Qui bien sûr ne compile pas. Pour les besoins de mon programme, j'aimerais initialiser un nombre arbitraire d'éléments dans flags , en fonction de la longueur d'un paramètre ( std :: vector , C- style array, ou autre) passé dans le constructeur de la structure.

Maintenant, j'aimerais utiliser un std :: array à l'intérieur de la structure, car la structure elle-même est créée plusieurs fois (donc un std :: vector ne serait pas idéal ici en raison de nombreuses allocations / désallocations), mais le nombre de valeurs dans flags qui doivent être initialisées n'est pas toujours le même.

Est-il possible d'initialiser un nombre spécifique de champs dans flags en fonction de la taille du conteneur de séquence passé comme paramètre dans le constructeur? p>


2 commentaires

Que se passe-t-il si initialFlagValues ​​ est trop grand? Qu'arrivera-t-il aux éléments restants si initialFlagValues ​​ est inférieur à 8 , sont-ils censés être mis à zéro ou laissés indéterminés? Voulez-vous une initialisation spécifique ou une affectation serait-elle également correcte? La dernière question n'a pas d'importance si le type d'élément est unsigned int , mais peut avoir de l'importance pour les types qui ne sont pas assignables.


Notez que faire une donnée membre const est une décision qui vient avec beaucoup de répercussions (pas d'affectation, par exemple). Vous ne devriez le faire que lorsque le besoin est démontré.


4 Réponses :


1
votes

std :: array est un agrégat. Cela signifie que la seule façon de l'initialiser est avec une braced_init_list ( {} ). Il n'y a aucun moyen de convertir un std :: vector en une braced_init_list donc une chose que vous pouvez faire est d'utiliser une boucle à l'intérieur du constructeur comme

Flags(std::vector<unsigned int> initialFlagValues) : flags{} // zero out flags
{
    auto size = std::min(initialFlagValues.size(), flags.size())
    for (size_t i = 0; i < size; ++i)
        flags[i] = initialFlagValues[i];
}


0 commentaires

3
votes

Utilisez une fonction d'assistance, peut-être sous la forme d'un lambda:

Flags(std::vector<unsigned int> initialFlagValues) :
    flags([](const auto& init) {
             std::array<unsigned int, 8> flags;
             // bounds check omitted for brevity
             std::copy(init.begin(), init.end(), flags.begin());
             return flags;
         }(initialFlagValues))
{}


0 commentaires

1
votes

Vous pouvez également tout faire au (presque) moment de la compilation, sans itération:

#include <iostream>
#include <type_traits>
#include <iterator>

struct Omg {
        static constexpr std::size_t SIZE = 8;

        template<class Container> Omg(Container &&c)
                : Omg(std::forward<Container>(c), std::make_index_sequence<SIZE>{})
        {}

        void omg() const {
                for(auto i: array) std::cout << i << ' ';
                std::cout << '\n';
        }
private:
        template<class C, std::size_t... is> Omg(C &&c, std::index_sequence<is...>)
                : array{get<is>(std::forward<C>(c))...}
        {}

        template<std::size_t i, class C> static constexpr auto get(C &&c) {
                return i < std::size(c)? c[i] : 0;
        }

        std::array<int, SIZE> array;
};

int main() {
        Omg(std::vector<int>{0, 1, 2, 3}).omg();
        int nyan[] = {42, 28, 14};
        Omg(nyan).omg();
}


0 commentaires

2
votes

Peut-être que la fonction d'assistance peut être un constructeur délégué

struct Flags
 {
   std::array<unsigned int, 8u> const flagsArr;

   template <typename T, std::size_t ... Is>
   Flags (T const & iFV, std::index_sequence<Is...>)
      : flagsArr{ Is < std::size(iFV) ? iFV[Is] : 0u ... }
    {}

   template <typename T>
   Flags (T const & iFV)
      : Flags{iFV, std::make_index_sequence<8u>{}}
    {}
};

Vous pouvez généraliser avec pour les types génériques prenant en charge les opérateurs [] et std :: size ( ) (ainsi que les tableaux de style C) comme suit

struct Flags
 {
   std::array<unsigned int, 8u> const flagsArr;

   template <std::size_t ... Is>
   Flags (std::vector<unsigned int> iFV, std::index_sequence<Is...>)
      : flagsArr{ Is < iFV.size() ? iFV[Is] : 0u ... }
    {}

   Flags (std::vector<unsigned int> iFV)
      : Flags{iFV, std::make_index_sequence<8u>{}}
    {}
};


0 commentaires