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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5549524/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 18:25:00  来源:igfitidea点击:

How do I initialize a member array with an initializer_list?

c++arraysc++11initializer-list

提问by swestrup

I'm getting up to speed with C++0x, and testing things out with g++ 4.6

我正在加快使用 C++0x,并使用 g++ 4.6 进行测试

I just tried the following code, thinking it would work, but it doesn't compile. I get the error:

我只是尝试了以下代码,认为它可以工作,但无法编译。我收到错误:

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

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

回答by JohannesD

You can use a variadic template constructor instead of an initializer list constructor:

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

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: If you can live without constness,another way would be to skip initialization and fill the array in the function body:

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

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

This way, though, you lose the compile-time bounds checking that the former solution provides.

但是,通过这种方式,您将失去前一个解决方案提供的编译时边界检查。

回答by Potatoswatter

As far as I can tell, using list-initialization of the function argument of the constructor (8.5.4/1) should be legal and solves many of the issues of the above. However, GCC 4.5.1 on ideone.com fails to match the constructor and rejects it.

据我所知,使用构造函数(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


If you really insist on initializer_list, you can use reinterpret_castto turn the underlying array of the initializer_listinto a C-style array.

如果你真的坚持initializer_list,你可以使用reinterpret_cast将 的底层数组initializer_list变成C风格的数组。

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

回答by haohaolee

According to the discussion here:

根据这里的讨论:

the right syntax for Potatoswatter's second solution is:

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

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

a little bit ugly, not consistent with common usage

有点丑,不符合一般用法

回答by Ivan Aksamentov - Drop

Just a small addition to great JohannesD answer.

只是JohannesD 回答的一个小补充。

In case of no arguments passed to fooconstructor, array will be default initialized. But sometimes you want to keep underlying array uninitilized (maybe for performance reasons). You cannot add default constructor along with variadic-templated one. Workaround is additional argument to variadic-templated constructor, to distinguish it from zero-argument constructor:

如果没有参数传递给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... } } {}
};

So, now you can brace-initilize object, or left it uninitialized:

所以,现在你可以大括号初始化对象,或者让它未初始化:

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


Update 31/07/2016

更新 31/07/2016

Three years have passed quickly and compiler implementers improved standard compliance of their products up to the level where default constructor is not considered ambiguous in the presence of variadic constructor anymore. So, in practice, we don't need additional T targument to variadic constructor to disambiguate constructors.

三年时间很快过去了,编译器实现者将他们产品的标准合规性提高到了默认构造函数在可变构造函数的存在下不再被认为是模棱两可的水平。因此,在实践中,我们不需要T t为可变参数构造函数添加额外的参数来消除构造函数的歧义。

Both

两个都

array2d() {}

and

array2d() = default;

will leave array uninitialized if object is being constructed without arguments. This behavior is consistent on all major compilers. Full example (rextester):

如果在没有参数的情况下构造对象,则数组将保持未初始化状态。这种行为在所有主要编译器上都是一致的。完整示例(reextester):

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

Output:

输出:

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 

Removing default constructor still leads to variadic constructor to be called and array being default-initialized (with all-zeros in our case).

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

Thanks @Alek for bumping this thread and for drawing attention to these facts, and also thanks to all people working hard on compiler development.

感谢 @Alek 提出这个话题并引起人们对这些事实的关注,也感谢所有在编译器开发方面努力工作的人。

回答by Bo Persson

You can't, arrays are not like other types (and don't have constructors taking a std::initializer_list).

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

Try this instead:

试试这个:

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

回答by franjesus

While this does not work:

虽然这不起作用:

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

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

Foo f = {1,3};

I found this simple approach to work nicely:

我发现这种简单的方法可以很好地工作:

struct Foo
{
  const int data[2];

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

Foo f = {1,3};

Of course the variadic template approach is probably better if you have a lot of elements, but in this simple case, this will probably suffice.

当然,如果您有很多元素,可变参数模板方法可能会更好,但在这种简单的情况下,这可能就足够了。

That is, if you want to explicitly define the constructor from initializer lists. For most POD cases this is fine and dandy:

也就是说,如果您想从初始化列表中显式定义构造函数。对于大多数 POD 案例,这很好,很花哨:

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

回答by Snps

If you don't care about bounds checking, then the following will work.

如果您不关心边界检查,那么以下将起作用。

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

回答by Erik Schnetter

You can define a constexprfunction that converts an initializer list to an array. The last (third) function is the one you call. The other create recursively a template parameter pack from the initializer list, and create the array once sufficiently many list elements have been read.

您可以定义一个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());
}