std :: initializer_list和c array []的优缺点是什么?

dar*_*une 8 c++ initializer-list constexpr c++17

假设我有一些假设的结构:

struct X {
  int i;
  double d;
}
Run Code Online (Sandbox Code Playgroud)

然后我可以写

constexpr X x_c_array[]{{5, 6.3}};
Run Code Online (Sandbox Code Playgroud)

要么

constexpr std::initializer_list<X> x_ilist{{5, 6.3}};
Run Code Online (Sandbox Code Playgroud)

auto不可能使用-编译器必须知道内部类型

这两个版本都有缺点吗?

更新:

同样值得关注的是,您是否能够使用/将一种类型转换为另一种类型-例如。在构造标准容器时?

Col*_*mbo 5

简单明了:initializer_list不是容器。这是外部分配元素的不变视图。完全不适合容器将在其中使用的任何情况-考虑不必要的间接(无可调整性),不变性,其名称的惯用语。最重要的是,它没有适当的接口。

似乎都合适的情况是序列的构造函数参数。如果长度是固定的(或模板参数化),则int const (&arr)[N]可以,尽管initializer_list它更简单,更灵活。毕竟,这就是它的设计意图。

  • @darune我的答案简洁明了,肯定是客观的(向我显示我发表个人观点的地方),并且比您接受的两页说明要少。我不需要参考,因为我认为我的解释是权威的。缺乏细节只是与周围膨胀的答案形成对比。“无论如何,我会指出您的关注点。” 完全没有加载。 (2认同)

max*_*x66 2

正如评论中所写,这是一个广泛的论点。

无论如何,我提请您注意一点。

第一种情况

X x1[] {{5, 6.3}};
Run Code Online (Sandbox Code Playgroud)

的元素数量x1是类型的一部分x1

所以你有那个

X x1[] {{5, 6.3}};
X x2[] {{5, 6.3}, {7, 8.1}};

static_assert( false == std::is_same<decltype(x1), decltype(x2)>::value );
Run Code Online (Sandbox Code Playgroud)

使用初始化列表

std::initializer_list<X> x3 {{5, 6.3}};
std::initializer_list<X> x4 {{5, 6.3}, {7, 8.1}};

static_assert( true == std::is_same<decltype(x3), decltype(x4)>::value );
Run Code Online (Sandbox Code Playgroud)

类型保持不变,改变元素的数量。

根据您的需求,这可能是第一个或第二个解决方案的优势。

事实上,元素的数量是 C 风格数组类型的一部分,这在元编程中可能是一个小小的优势。

假设您想要一个返回i数组值之和的函数,您可以使用 C 风格数组编写

template <std::size_t N, std::size_t ... Is>
constexpr auto sum_i_helper (X const (&xArr)[N], std::index_sequence<Is...>)
 { return (... + xArr[Is].i); }

template <std::size_t N>
constexpr auto sum_i (X const (&xArr)[N])
 { return sum_i_helper(xArr, std::make_index_sequence<N>{}); }
Run Code Online (Sandbox Code Playgroud)

当 的参数sum_i()是非 constexpr 值时,该函数也会编译。

如果您想编写类似的内容,std::initializer_list则稍微复杂一些,因为size()列表的 不一定是编译时已知值,所以或者您将其作为模板参数传递(但该函数不适用于运行时列表)或者您size()在函数内部使用,但不能使用它来初始化std::index_sequence.

无论如何,使用初始化列表,您可以使用旧的for()循环

constexpr auto sum_i (std::initializer_list<X> const lx)
 { 
   int ret { 0 };

   for ( auto const & x : lx )
      ret += x.i;

   return ret;
 }
Run Code Online (Sandbox Code Playgroud)

lx当是一个值时,该函数可以计算编译时间constexpr

另外值得关注的是,您是否能够使用/将一种类型转换为另一种类型 - 例如。建造标准集装箱时?

将数组转换为初始值设定项列表很容易,并且可以使用编译时和运行时已知值

template <std::size_t N, std::size_t ... Is>
constexpr auto convertX_h (X const (&xArr)[N], std::index_sequence<Is...>)
 { return std::initializer_list<X>{ xArr[Is]... }; }

template <std::size_t N>
constexpr auto convertX (X const (&xArr)[N])
 { return convertX_h(xArr, std::make_index_sequence<N>{}); }

// ....

X x1[] {{5, 6.3}};

std::initializer_list<X> x5 = convertX(x1);
Run Code Online (Sandbox Code Playgroud)

将初始值设定项列表转换为 C 样式数组更加困难,因为数组的类型取决于元素的数量,因此您需要在编译时知道初始值设定项列表中的元素数量,因为您无法随机访问一个初始值设定项列表,更糟糕的是,因为您无法编写返回 C 样式数组的函数。

我可以想象一个如下的解决方案,将初始化列表转换为std::array(题外话建议:std::array如果可能的话,使用 ,而不是 C 样式数组)

template <std::size_t N>
constexpr auto convertX (std::initializer_list<X> const lx)
 { 
   std::array<X, N> ret;

   std::size_t i { 0u };

   for ( auto const & x : lx )
      ret[i++] = x;

   return ret;
 }

// ...

constexpr std::initializer_list<X> x4 {{5, 6.3}, {7, 8.1}};

auto x6 = convertX<x4.size()>(x4);
Run Code Online (Sandbox Code Playgroud)

x6now 是 a std::array<X, 2>,而不是 a X[2],并且x4必须是一个constexpr值。