如何使用initializer_list初始化成员数组?

时间:2011-04-05 09:02:46

标签: c++ arrays c++11 initializer-list

我正在快速使用C ++ 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};

8 个答案:

答案 0 :(得分:52)

您可以使用可变参数模板构造函数而不是初始化列表构造函数:

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
}

编辑:如果你可以没有常量,另一种方法是跳过初始化并填充函数体中的数组:

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

但是,这样会丢失前一个解决方案提供的编译时边界检查。

答案 1 :(得分:17)

据我所知,使用构造函数(8.5.4 / 1)的函数参数的列表初始化应该是合法的并且解决了上述的许多问题。但是,ideone.com上的GCC 4.5.1无法与构造函数匹配并拒绝它。

#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_castinitializer_list的基础数组转换为C风格的数组。

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

答案 2 :(得分:5)

根据讨论here

Potatoswatter第二个解决方案的正确语法是:

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

有点难看,与常见用法不一致

答案 3 :(得分:4)

只是杰出JohannesD answer的一小部分内容。

如果没有参数传递给foo构造函数,则数组将默认初始化。但有时你想保持底层阵列不受限制(可能出于性能原因)。您不能添加默认构造函数和variadic-templated。 变通方法是variadic-templated构造函数的附加参数,以区别于零参数构造函数:

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

所以,现在你可以使用boot-initilize对象,或者保持未初始化状态:

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

2016年7月31日更新

三年过去了很快,编译器实现者改进了他们产品的标准合规性,直到在存在可变参数构造函数的情况下默认构造函数不被认为是模糊的。因此,在实践中,我们不需要为可变参数构造函数添加额外的T t参数来消除构造函数的歧义。

两个

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 

删除默认构造函数仍然会导致调用可变参数构造函数并且数组被默认初始化(在我们的例子中使用全零)。

感谢@Alek提出这个问题并提请注意这些事实,并感谢所有在编译器开发上努力的人。

答案 4 :(得分:1)

你不能,数组不像其他类型(并且没有构造函数采用std :: initializer_list)。

请改为尝试:

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

答案 5 :(得分:0)

虽然这不起作用:

#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};

答案 6 :(得分:0)

如果您不关心边界检查,则以下内容将起作用。

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

答案 7 :(得分:0)

您可以定义一个constexpr函数,该函数将初始化列表转换为数组。最后一个(第三个)函数是您调用的函数。另一个从初始化器列表中递归创建模板参数包,并在读取足够多的列表元素后创建数组。

template <typename T, size_t N, typename... Ts>
constexpr enable_if_t<(sizeof...(Ts) == N), array<T, N> >
array_from_initializer_list(const T *const beg, const T *const end,
                            const Ts... xs) {
  return array<T, N>{xs...};
}

template <typename T, size_t N, typename... Ts>
constexpr enable_if_t<(sizeof...(Ts) < N), array<T, N> >
array_from_initializer_list(const T *const beg, const T *const end,
                            const Ts... xs) {
  return array_from_initializer_list<T, N>(beg + 1, end, *beg, xs...);
}

template <typename T, size_t N>
constexpr array<T, N> array_from_initializer_list(initializer_list<T> l) {
  return array_from_initializer_list<T, N>(l.begin(), l.end());
}