【问题标题】:Using std::array with initialization lists使用带有初始化列表的 std::array
【发布时间】:2012-01-01 19:02:31
【问题描述】:

除非我弄错了,否则应该可以通过以下方式创建 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 东西)但是,无论是否有错误,我认为这是因为您使用 std::string 和字符串文字。你试过用std::string()包装字符串文字吗?
  • @Chris:这适用于 Mac OSX 10.6 上的 gcc 4.6.1。您使用什么编译器选项?
  • @juanchopanza:我正在使用 MinGW-w64 的 TDM 发行版,它有 GCC 4.6.1,我正在使用 -std=c++0x。
  • @Chris。有趣的。我有 4.6.1 的 macports 版本,它使用相同的编译器标志。
  • 两者都适用于 gcc-4.6.1 linux 以及带有两组大括号的版本,这确实是最“正确”的。 gcc-4.7 上的所有三个 ICE。提交错误报告。

标签: c++ c++11 libstdc++


【解决方案1】:

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

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

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

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

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

【讨论】:

  • 标准委员会是故意这样做的吗?
  • 为什么标准不只要求std::array 有一个与初始化列表一起使用的构造函数?
  • @PaulManta:因为那样它就没有资格进行聚合初始化。聚合初始化可以在编译时折叠,具体取决于数组元素的类型(std::string 不合格)。 Initializer 列表初始化必须是运行时函数调用,与数组元素的类型无关。
  • @Dani:不一定是“故意的”。这只是事情如何运作的必要产物。 std::array 旨在作为编译时类型,因此它需要与聚合初始化一起使用。
  • 好的,我在 Wikipedia en.wikipedia.org/wiki/… 上找到了这个。请注意它是如何表示的请注意,对于符合标准的编译器,可以使用更少的大括号(根据标准的 8.5.1 (11))。所以看来 GNU 团队有点搞砸了,应该能够将 std::array 初始化为 std::array<:string> strings = { "a", "b " };
【解决方案2】:

添加到已接受的答案:

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 function ‘int 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 实现的一个怪癖。

【讨论】:

    【解决方案3】:

    游戏有点晚了,但这就是我在 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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-06
      • 2021-10-01
      • 2015-10-05
      • 2015-09-04
      • 1970-01-01
      • 1970-01-01
      • 2012-02-10
      • 2017-04-17
      相关资源
      最近更新 更多