web-dev-qa-db-fra.com

Comment initialiser un tableau de membres avec une liste d'initialisation?

Je me familiarise avec C++ 0x et je teste des choses avec g ++ 4.6

J'ai juste essayé le code suivant, pensant que cela fonctionnerait, mais il ne compile pas. Je reçois l'erreur:

incompatible types in assignment of ‘std::initializer_list<const int>’ to ‘const int [2]’

struct Foo
  {
    int const data[2];

    Foo(std::initializer_list<int const>& ini)
    : data(ini)
    {}
  };

Foo f = {1,3};
43
swestrup

Vous pouvez utiliser un constructeur de modèle variadique au lieu d'un constructeur de liste d'initialiseur:

struct foo { 
    int x[2]; 
    template <typename... T> 
    foo(T... ts) : x{ts...} { // note the use of brace-init-list
    } 
};

int main() {
    foo f1(1,2);   // OK
    foo f2{1,2};   // Also OK
    foo f3(42);    // OK; x[1] zero-initialized
    foo f4(1,2,3); // Error: too many initializers
    foo f5(3.14);  // Error: narrowing conversion not allowed
    foo f6("foo"); // Error: no conversion from const char* to int
}

EDIT: Si vous pouvez vivre sans constance, une autre façon serait de sauter l’initialisation et de remplir le tableau dans le corps de la fonction

struct foo {
    int x[2]; // or std::array<int, 2> x;
    foo(std::initializer_list<int> il) {
       std::copy(il.begin(), il.end(), x);
       // or std::copy(il.begin(), il.end(), x.begin());
       // or x.fill(il.begin());
    }
}

De cette façon, vous perdez les limites de compilation en vérifiant ce que l'ancienne solution fournit.

50
JohannesD

Autant que je sache, utiliser l'initialisation de liste de l'argument de fonction du constructeur (8.5.4/1) devrait être légal et résoudre bon nombre des problèmes ci-dessus. Cependant, GCC 4.5.1 sur ideone.com ne correspond pas au constructeur et le rejette.

#include <array>

struct Foo
  {
    std::array< int, 2 > const data;

    Foo(std::array<int, 2> const& ini) // parameter type specifies size = 2
    : data( ini )
    {}
  };

Foo f( {1,3} ); // list-initialize function argument per 8.5.4/1

Si vous insistez vraiment sur initializer_list, vous pouvez utiliser reinterpret_cast pour transformer le tableau sous-jacent du initializer_list en un tableau de style C.

Foo(std::initializer_list<int> ini) // pass without reference- or cv-qualification
: data( reinterpret_cast< std::array< int, 2 > const & >( * ini.begin() )
16
Potatoswatter

Selon la discussion ici :

la syntaxe correcte pour la deuxième solution de Potatoswatter est la suivante:

Foo f( {{1,3}} ); //two braces

un peu moche, pas conforme à l'usage courant

5
haohaolee

Juste un petit ajout à la grande JohannesD répondre

Si aucun argument n'est passé au constructeur foo, array sera initialisé par défaut. Mais parfois, vous voulez garder le tableau sous-jacent non initié (peut-être pour des raisons de performances). Vous ne pouvez pas ajouter de constructeur par défaut avec un modèle basé sur variadic. La solution de contournement est un argument supplémentaire pour le constructeur à modèles variadiques, permettant de le distinguer du constructeur à argument zéro:

template<class T, size_t rows, size_t cols>
class array2d
{
    std::array<T, rows * cols> m_Data;
    public:

    array2d() {}

    template <typename T, typename... Types>
    array2d(T t, Types... ts) : m_Data{ { t, ts... } } {}
};

Donc, maintenant vous pouvez initier un objet, ou le laisser non initialisé:

array2d<int, 6, 8> arr = { 0, 1, 2, 3 };  // contains 0, 1, 2, 3, 0, 0, 0, ...
array2d<int, 6, 8> arr2;                  // contains garbage

Mise à jour 31/07/2016

Trois années se sont écoulées rapidement et les développeurs de compilateurs ont amélioré la conformité standard de leurs produits jusqu'au niveau où le constructeur par défaut n'est plus considéré comme ambigu en présence de constructeur variadique. Donc, en pratique, nous n’avons pas besoin d’un argument supplémentaire T t avec un constructeur variadique pour lever l’ambiguïté des constructeurs.

Tous les deux

array2d() {}

et

array2d() = default;

laissera un tableau non initialisé si l'objet est construit sans arguments. Ce comportement est cohérent sur tous les principaux compilateurs. Exemple complet ( rextester ):

#include <array>
#include <iostream>

template<class T, size_t rows, size_t cols>
class array2d
{
  public:
    std::array<T, rows * cols> m_Data;

    array2d() = default;

    template <typename... Types>
    array2d(Types... ts) : m_Data{ { ts... } } {}
};

int main()
{
    array2d<int, 6, 8> arr_init = { 0, 1, 2, 3 };
    array2d<int, 6, 8> arr_default;


    std::cout << "Initialized: \n";
    for(const auto& a : arr_init.m_Data)
        std::cout << a << " ";
    std::cout << "\n";

    std::cout << "Default: \n";
    for(const auto& a : arr_default.m_Data)    
        std::cout << a << " ";

    std::cout << "\n";
}

Sortie:

Initialized: 
0 1 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
Default: 
2 0 -519559849 32558 1 32558 0 0 -519634912 32558 -526739248 32558 1 0 2 0 6295032 0 -519531243 32558 0 0 -1716075168 32765 6295648 0 4196192 0 6295648 0 -526527271 32558 1 0 2 0 6295032 0 4196845 0 124 0 0 0 4196768 0 4196518 0 

La suppression du constructeur par défaut conduit toujours à appeler le constructeur variadique et à initialiser le tableau par défaut (dans ce cas, nous utilisons uniquement des zéros).

Merci à @Alek d'avoir bousillé ce fil et d'avoir attiré l'attention sur ces faits, et merci également à toutes les personnes qui travaillent dur au développement du compilateur.

4

Vous ne pouvez pas, les tableaux ne sont pas comme les autres types (et les constructeurs ne prennent pas std :: initializer_list).

Essayez ceci à la place:

struct Foo  
{  
  const std::vector<int>   data;
  Foo(std::initializer_list<int> ini) : data(ini)
  {}
}; 
1
Bo Persson

Bien que cela ne fonctionne pas:

#include <initializer_list>
struct Foo
{
  const int data[2];

  constexpr Foo(const std::initializer_list<int>& ini): data{ini}  {}
};

Foo f = {1,3};

J'ai trouvé cette approche simple pour bien fonctionner:

struct Foo
{
  const int data[2];

  constexpr Foo(const int a, const int b): data{a,b}  {}
};

Foo f = {1,3};

Bien sûr, l’approche par modèles variadiques est probablement préférable si vous avez beaucoup d’éléments, mais dans ce cas simple, cela suffira probablement.

En d’autres termes, si vous souhaitez définir explicitement le constructeur à partir de listes d’initialisation. Pour la plupart des cas de POD, tout va bien:

struct Foo
{
  const int data[2];
};
Foo f = {1,3};
0
franjesus

Si vous ne vous souciez pas de la vérification des limites, alors ce qui suit fonctionnera.

struct Foo {
    int const data[2];
    Foo(std::initializer_list<int> ini)
        : data{*std::begin(ini), *std::next(std::begin(ini), 1)} {}
};
0
Snps