Подтвердить что ты не робот

Как инициализировать массив-член с помощью initializer_list?

Я получаю скорость с С++ 0x и проверяю вещи с помощью g++ 4.6

Я просто попробовал следующий код, считая, что это сработает, но он не компилируется. Я получаю сообщение об ошибке:

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};
4b9b3361

Ответ 1

Вместо конструктора списка инициализаторов можно использовать конструктор конструкторов переменных:

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: если вы можете жить без константы, другим способом было бы пропустить инициализацию и заполнить массив в теле функции:

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());
    }
}

Таким образом, вы теряете проверку границ времени компиляции, которую предоставляет предыдущее решение.

Ответ 2

Насколько я могу судить, использование инициализации списка аргумента функции конструктора (8.5.4/1) должно быть законным и решает многие из вышеперечисленных вопросов. Однако GCC 4.5.1 на ideone.com не соответствует конструктору и отклоняет его.

#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

Если вы действительно настаиваете на initializer_list, вы можете использовать reinterpret_cast, чтобы превратить базовый массив initializer_list в массив C-style.

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

Ответ 3

В соответствии с обсуждением здесь:

правильный синтаксис второго решения Potatoswatter:

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

немного уродливый, несовместимый с обычным использованием

Ответ 4

Просто небольшое дополнение к замечанию JohannesD.

В случае отсутствия аргументов, переданных конструктору foo, массив будет инициализирован по умолчанию. Но иногда вы хотите сохранить базовый массив неинициализированным (возможно, по соображениям производительности). Вы не можете добавить конструктор по умолчанию вместе с вариативно-шаблонным. Обходной путь является дополнительным аргументом для конструктора с вариационной схемой, чтобы отличить его от конструктора с нулевым аргументом:

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

Итак, теперь вы можете скопировать инициализацию объекта или оставить его неинициализированным:

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

Обновление 31/07/2016

Прошло три года, и разработчики компиляторов улучшили стандартную совместимость своих продуктов до уровня, где конструктор по умолчанию больше не считается неоднозначным в присутствии вариационного конструктора. Таким образом, на практике нам не нужен дополнительный аргумент T t для конструктора Variadic для устранения неоднозначности конструкторов.

И

array2d() {}

и

array2d() = default;

оставит массив неинициализированным, если объект строится без аргументов. Такое поведение совместимо со всеми основными компиляторами. Полный пример (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";
}

Вывод:

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 

Удаление конструктора по умолчанию по-прежнему приводит к вызову конструктора Variadic, и инициализируется инициализация по умолчанию (со всеми нулями в нашем случае).

Спасибо @Alek для натыкаясь эту тему и привлечь внимание к этим фактам, а также благодарность всем людям, работающим трудно по разработке компилятора.

Ответ 5

Вы не можете, массивы не похожи на другие типы (и не имеют конструкторов, принимающих std:: initializer_list).

Попробуйте это вместо:

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

Ответ 6

Пока это не работает:

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

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

Foo f = {1,3};

Я нашел этот простой подход к работе хорошо:

struct Foo
{
  const int data[2];

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

Foo f = {1,3};

Конечно, вариант с вариационным шаблоном, вероятно, лучше, если у вас много элементов, но в этом простом случае этого, вероятно, будет достаточно.

То есть, если вы хотите явно определить конструктор из списков инициализаторов. Для большинства случаев POD это нормально и денди:

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

Ответ 7

Если вам не нужна проверка границ, тогда будет работать следующее.

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