¿Cómo inicializo una matriz miembro con una lista inicializadora?


Me estoy poniendo al día con C++0x, y probando cosas con g++ 4.6

Acabo de probar el siguiente código, pensando que funcionaría, pero no compila. Me sale el error:

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};
Author: swestrup, 2011-04-05

7 answers

Puede usar un constructor de plantilla variadic en lugar de un constructor de lista inicializador:

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
}

EDITAR: Si puede vivir sin constancia, otra forma sería omitir la inicialización y llenar el array en el cuerpo de la función:

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 esta manera, sin embargo, se pierden los límites de tiempo de compilación comprobando que la solución anterior proporciona.

 48
Author: JohannesD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-07 19:53:13

Por lo que puedo decir, usar la inicialización de lista del argumento de la función del constructor (8.5.4/1) debería ser legal y resolver muchos de los problemas de lo anterior. Sin embargo, el CCG 4.5.1 sobre ideone.com no coincide con el constructor y lo rechaza.

#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 realmente insiste en initializer_list, puede usar reinterpret_cast para convertir la matriz subyacente de initializer_list en una matriz de estilo C.

Foo(std::initializer_list<int> ini) // pass without reference- or cv-qualification
: data( reinterpret_cast< std::array< int, 2 > const & >( * ini.begin() )
 15
Author: Potatoswatter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-20 23:58:12

De acuerdo con la discusión aquí :

La sintaxis correcta para la segunda solución de Potatoswatter es:

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

Un poco feo, no consistente con el uso común

 5
Author: haohaolee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-11 18:19:10

Solo una pequeña adición a la gran respuesta de JohannesD.

En caso de que no se pasen argumentos al constructor foo, la matriz se inicializará por defecto. Pero a veces desea mantener la matriz subyacente sin itilizar (tal vez por razones de rendimiento). No puede agregar el constructor predeterminado junto con uno con plantilla variadic. Workaround es un argumento adicional al constructor variadic-templated, para distinguirlo del constructor zero-argument:

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... } } {}
};

Así que, ahora puedes brace-inicializar objeto, o dejarlo sin inicializar:

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

Actualización 31/07/2016

Tres años han pasado rápidamente y los implementadores de compiladores mejoraron el cumplimiento estándar de sus productos hasta el nivel donde el constructor predeterminado ya no se considera ambiguo en presencia del constructor variadic. Entonces, en la práctica, no necesitamos un argumento adicional T t al constructor variadic para desambiguar constructor.

Ambos

array2d() {}

Y

array2d() = default;

Dejará el array sin inicializar si el objeto se está construyendo sin argumentos. Este comportamiento es consistente en todos los compiladores principales. Ejemplo completo (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";
}

Salida:

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 eliminación del constructor predeterminado todavía conduce a que se llame al constructor variadic y a que la matriz se inicialice por defecto (con todos los ceros en nuestro caso).

Gracias @Alek por golpear este hilo y por dibujar atención a estos hechos, y también gracias a todas las personas que trabajan duro en el desarrollo del compilador.

 4
Author: Ivan Aksamentov - Drop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:03

No se puede, los arrays no son como otros tipos (y no tienen constructores que tomen un std::initializer_list).

Prueba esto en su lugar:

struct Foo  
{  
  const std::vector<int>   data;
  Foo(std::initializer_list<int> ini) : data(ini)
  {}
}; 
 1
Author: Bo Persson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-04 12:02:17

, Mientras que esto no funciona:

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

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

Foo f = {1,3};

Encontré que este enfoque simple funciona muy bien:

struct Foo
{
  const int data[2];

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

Foo f = {1,3};

Por supuesto, el enfoque de plantilla variádica es probablemente mejor si tiene muchos elementos, pero en este simple caso, esto probablemente será suficiente.

Es decir, si desea definir explícitamente el constructor a partir de listas inicializadoras. Para la mayoría de los casos POD esto está bien y dandy:

struct Foo
{
  const int data[2];
};
Foo f = {1,3};
 0
Author: franjesus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-31 15:58:27

Si no le importa la comprobación de límites, entonces lo siguiente funcionará.

struct Foo {
    int const data[2];
    Foo(std::initializer_list<int> ini)
        : data{*std::begin(ini), *std::next(std::begin(ini), 1)} {}
};
 0
Author: Snps,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-31 16:46:48