如何使用初始化列表构造std::array对象?

42

可能是重复问题:
如何使用初始化列表初始化成员数组?

使用初始化列表可以很好地构造一个std::array:

std::array<int, 3> a = {1, 2, 3};  // works fine

但是,当我尝试将其作为类的数据成员或基类对象从std::initializer_list构造时,它并不起作用:

#include <array>
#include <initializer_list>

template <typename T, std::size_t size, typename EnumT>
struct enum_addressable_array : public std::array<T, size>
{
    typedef std::array<T, size> base_t;
    typedef typename base_t::reference reference;
    typedef typename base_t::const_reference const_reference;
    typedef typename base_t::size_type size_type;

    enum_addressable_array(std::initializer_list<T> il) : base_t{il} {}

    reference operator[](EnumT n)
    {
        return base_t::operator[](static_cast<size_type>(n));
    }

    const_reference operator[](EnumT n) const
    {
        return base_t::operator[](static_cast<size_type>(n));
    }
};

enum class E {a, b, c};
enum_addressable_array<char, 3, E> ea = {'a', 'b', 'c'};

gcc 4.6的错误:

test.cpp: In constructor 'enum_addressable_array<T, size, EnumT>::enum_addressable_array(std::initializer_list<T>) [with T = char, unsigned int size = 3u, EnumT = E]':
test.cpp:26:55:   instantiated from here
test.cpp:12:68: error: no matching function for call to 'std::array<char, 3u>::array(<brace-enclosed initializer list>)'
test.cpp:12:68: note: candidates are:
include/c++/4.6.1/array:60:12: note: std::array<char, 3u>::array()
include/c++/4.6.1/array:60:12: note:   candidate expects 0 arguments, 1 provided
include/c++/4.6.1/array:60:12: note: constexpr std::array<char, 3u>::array(const std::array<char, 3u>&)
include/c++/4.6.1/array:60:12: note:   no known conversion for argument 1 from 'std::initializer_list<char>' to 'const std::array<char, 3u>&'
include/c++/4.6.1/array:60:12: note: constexpr std::array<char, 3u>::array(std::array<char, 3u>&&)
include/c++/4.6.1/array:60:12: note:   no known conversion for argument 1 from 'std::initializer_list<char>' to 'std::array<char, 3u>&&'

如何使我的包装类可以使用初始化列表进行初始化,例如:

enum_addressable_array<char, 3, E> ea = {'a', 'b', 'c'};
3个回答

40

std::array<>没有接受std::initializer_list<>(初始化列表构造函数)的构造函数,并且没有特殊的语言支持,可以将std::initializer_list<>传递给类的构造函数,以便它可以正常工作。因此会失败。

为了使其工作,您的派生类需要捕获所有元素,然后将它们转发到一个构造函数模板中:

template<typename ...E>
enum_addressable_array(E&&...e) : base_t{{std::forward<E>(e)...}} {}

请注意,在这种情况下你需要使用{{...}},因为花括号省略(像你的情况一样省略花括号)在那个位置不起作用。只有在形式为T t = { ... }的声明中才被允许使用。因为std::array<>由嵌入原始数组的结构体组成,因此需要两层花括号。不幸的是,我认为std::array<>的确切聚合结构未指定,因此您需要希望它在大多数实现上都能正常工作。

8
Clang 抱怨输入应该是 E&& ...e。(有时候,g++ 告诉我去杀人。) - John McFarlane
1
会不会覆盖复制构造函数之类的呢?能不能更加限制一下呢?比如template<typename ...E, class = decltype(base_t{{std::forward<Args__>(std::declval<Args__>()...)...}}> enum_addressable_array(E&&...e) : base_t{{std::forward<E>(e)...}} {}(我无法让它正常工作) - alfC
1
@alfC 模板永远不会比隐式生成的特殊成员函数更匹配,因为它们是精确匹配(即使它们被隐式或显式声明为已删除)。然而,棘手的部分是,如果您传递类似于“enum_addressable_array&”的内容,则这将是更好的匹配(因此在重载决策中更受欢迎)。 - David Stone
我有一个问题。你说:“不幸的是,我认为std :: array <>的确切聚合结构未指定,因此您需要希望它在大多数实现上都能正常工作。”但是,根据http://en.cppreference.com/w/cpp/concept/SequenceContainer#cite_note-1,`std::array`需要支持花括号初始化列表分配。这是否意味着您的解决方案需要按照C ++标准工作,而不必依赖于实现细节?我只是在问,而不是说它必须工作:这种先进水平处于我的理解前沿 :) - user4385532
1
@gaazkam 需要支持= { ... }初始化。但不需要支持= {{ .. }}初始化。然而,在成员初始化器的情况下,只有后者才能起作用。但由于不需要支持后者,并且前者仅需要支持= { ... }语法,因此在成员初始化器的情况下没有可移植的语法。然而,我不了解规范的最新修订。也许他们现在也支持这种情况。 - Johannes Schaub - litb

36

由于std::array是包含集合的结构体(它本身不是集合,也没有接受std::initializer_list的构造函数),因此您可以使用双括号语法初始化结构内部的基础集合,如下所示:

std::array<int, 4> my_array = {{1, 2, 3, 4}};

请注意,这不是使用 std::initializer_list ... 这只是使用 C++ 初始化列表来初始化公开可访问的 std::array 数组成员。

11

std::array 没有接受 std::initializer_list 的构造函数。这是一件好事,因为初始化列表可能比数组的固定大小还要大。

您可以通过测试初始化列表是否不大于数组的大小来进行初始化,然后使用 std::copy 将初始化列表的元素复制到 std::arrayelems 成员中。


当元素没有默认构造函数时,这种方法无法正常工作。 - Carlo Wood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接