使用初始化列表与 std::array

62

除非我弄错了,应该可以通过以下方式创建 std:array:

std::array<std::string, 2> strings = { "a", "b" };
std::array<std::string, 2> strings({ "a", "b" });

然而,使用GCC 4.6.1,我无法让这些任何一个起作用。编译器只是简单地说:

expected primary-expression before ',' token

然而使用初始化列表初始化std::vector却可以正常工作。那到底是哪个呢?我认为std::array应该接受初始化列表是我的错觉吗,还是GNU标准C++库团队出了差错?


我不确定这是否有效(我对0x的东西不是很了解),但是无论是否有bug,我认为问题出在你正在使用带有字符串字面量的std::string。你尝试过用std::string()包装字符串字面量吗? - John Chadwick
@Chris:在我的Mac OSX 10.6上,使用gcc 4.6.1可以正常工作。你使用了哪些编译器选项? - juanchopanza
@juanchopanza:我正在使用MinGW-w64的TDM发行版,其中包含GCC 4.6.1,并且我正在使用-std=c++0x。 - Chris_F
@Chris。有趣。我有 4.6.1的MacPorts版本和它可以使用相同的编译器标志。 - juanchopanza
两种方法在gcc-4.6.1 linux上都可以工作,使用两组大括号的版本实际上是最“正确”的。所有三个在gcc-4.7上都会产生内部编译错误。正在提交错误报告。 - emsr
我在第二个表单上犯了错误。它不能在gcc-4.6.1 Linux上编译。如下所述,这种行为是正确的。正如@NicolBolas所指出的那样,第二个无法工作。两个版本的第二个构造函数形式都会给出合理的错误提示。我搞混了。抱歉。 - emsr
3个回答

108

std::array 很有趣。它基本上是这样定义的:

template<typename T, int size>
struct std::array
{
  T a[size];
};

这是一个包含数组的结构体,它没有接受初始化列表的构造函数。但是根据C++11的规则,std::array是一个聚合体,因此可以通过聚合初始化来创建它。要在结构体内部对数组进行聚合初始化,需要使用第二组花括号:

std::array<std::string, 2> strings = {{ "a", "b" }};

请注意,标准建议在这种情况下可以省略额外的大括号。因此,这很可能是GCC的一个错误。

标准委员会是故意这样做的吗? - Daniel
为什么标准不直接要求 std::array 具有可以使用初始化列表的构造函数呢? - Paul Manta
6
因为这样就不能满足聚合初始化的要求。聚合初始化可以在编译时进行折叠,具体取决于数组元素的类型(std::string 不符合要求)。无论数组元素的类型如何,初始化列表初始化都必须是运行时函数调用。 - Nicol Bolas
2
@Dani:不一定是“故意的”。这只是事情必须工作的一个必然结果。std::array旨在成为编译时类型,因此需要与聚合初始化配合使用。 - Nicol Bolas
4
好的,我在维基百科上找到了这个页面 http://en.wikipedia.org/wiki/Array_%28C%2B%2B%29#Implementation_as_aggregate。请注意它说:*请注意,对于符合标准的编译器,可以使用更少的大括号(根据标准的8.5.1(11))。*因此,似乎GNU团队犯了一个小错误,**应该**能够像这样初始化std::array:std::array<std::string, 2> strings = { "a", "b" }; - Chris_F

13

补充已接受的答案:

std::array<char, 2> a1{'a', 'b'};
std::array<char, 2> a2 = {'a', 'b'};
std::array<char, 2> a3{{'a', 'b'}};
std::array<char, 2> a4 = {{'a', 'b'}};

所有工作都在GCC 4.6.3 (Xubuntu 12.01)上进行。然而,

void f(std::array<char, 2> a)
{
}

//f({'a', 'b'}); //doesn't compile
f({{'a', 'b'}});

上述代码需要使用双花括号编译。如果使用单花括号会导致以下错误:

../src/main.cc: In functionint main(int, char**)’:
../src/main.cc:23:17: error: could not convert ‘{'a', 'b'}’ from ‘<brace-enclosed initializer list>’ to ‘std::array<char, 2ul>’

我不确定类型推断/转换的哪个方面使事情以这种方式工作,或者这是否是GCC实现的怪癖。


4
有点晚了,但这是我在C++17中的做法。 不使用初始化器列表,只是一个变长值列表。 像这样:auto ar2 = create_array(1, 2, 3, 4);
#include <array>
#include <type_traits>

namespace traits
{

template<typename T, typename... Ts>
struct array_type
{
  using type = T;
};

template<typename T, typename... Ts>
static constexpr bool are_same_type()
{
  return std::conjunction_v<std::is_same<T, Ts>...>;
}

}

template<typename... T>
constexpr auto create_array(const T&&... values)
{
  using array_type = typename traits::array_type<T...>::type;
  static_assert(sizeof...(T) > 0, "an array must have at least one element");
  static_assert(traits::are_same_type<T...>(), "all elements must have same type");
  return std::array<array_type, sizeof...(T)>{ values... };
}

template<typename T, typename... Ts>
constexpr auto create_array_t(const Ts&&... values)
{
  using array_type = T;
  static_assert(sizeof...(Ts) > 0, "an array must have at least one element");
  static_assert(traits::are_same_type<Ts...>(), "all elements must have same type");
  return std::array<array_type, sizeof...(Ts)>{ static_cast<T>(values)... };
}

// to create a std::array of specific type
auto ar = create_array_t<std::uint8_t>(1u, 2u, 3u, 4u);
static_assert(ar.size() == 4);

// to create an array and let the compiler deduce its type
auto ar2 = create_array(1, 2, 3, 4);
static_assert(ar2.size() == 4);

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