繁体   English   中英

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

[英]How do I initialize a member array with an 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};

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

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

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

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

根据这里的讨论:

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

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

有点丑,不符合一般用法

只是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参数给可变参数构造函数来消除构造函数的歧义。

两个都

array2d() {}

array2d() = default;

如果在没有参数的情况下构造对象,则数组将保持未初始化状态。 这种行为在所有主要编译器上都是一致的。 完整示例( 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";
}

输出:

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 提出这个话题并引起人们对这些事实的关注,也感谢所有在编译器开发方面努力工作的人。

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

试试这个:

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

虽然这不起作用:

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM