11

编辑:在我们开始之前,这个问题不是关于正确使用std::initializer_list; 它是关于在需要方便的语法时应该传递的内容。感谢您留在主题上。


C++11 引入std::initializer_list了定义接受花括号初始化列表参数的函数。

struct bar {
    bar( char const * );
    bar( int );
} dog( 42 );

fn foo( std::initializer_list< bar > args );

foo( { "blah", 3, dog } );

语法很好,但在底层它由于各种问题而令人反感:

  • 它们不能被有意义地移动。上述函数必须dog从列表中复制;这不能转换为移动构造或省略。根本不能使用只移动类型。(嗯,const_cast实际上是一个有效的解决方法。如果有一篇关于这样做的文章,我想看看。)

  • 也没有constexpr语义。(这将在 C++1y 中实现。不过,这是一个小问题。)

  • const不像其他地方那样传播;initializer_list永远是,但它的const内容永远是。(因为它不拥有它的内容,它不能授予对副本的写访问权,尽管在任何地方复制它很少是安全的。)

  • initializer_list对象不拥有它的存储空间(哎呀);它与提供存储的完全独立的裸数组 (yikes) 的关系被模糊地定义 (yikes) 为对绑定临时的引用的关系 (quadruple yikes)。

我相信这些事情会在适当的时候得到修复,但现在有没有最佳实践来获得优势而不需要硬编码initializer_list?是否有任何关于解决直接依赖它的文献或分析?

显而易见的解决方案是通过值传递标准容器,例如std::vector. 一旦将对象从 复制到其中initializer_list,它就会被移动构造为按值传递,然后您可以将内容移出。一个改进是在堆栈上提供存储。一个好的库可能能够提供 、 和 的大部分优点,initializer_list甚至无需使用前者。arrayvector

有资源吗?

4

1 回答 1

6

它是关于在需要方便的语法时应该传递的内容。

如果您想要大小的便利(即:用户只需键入一个{}没有函数调用或单词的列表),那么您必须接受适当的所有权力和限制initializer_list。即使您尝试将其转换为其他东西,例如某种形式的array_ref,您仍然必须initializer_list在它们之间有一个中介。这意味着您无法解决遇到的任何问题,例如无法摆脱它们。

如果它通过initializer_list,那么您必须接受这些限制。因此,替代方法是不通过initializer_list,这意味着您将不得不接受某种形式的具有特定语义的容器。并且替代类型必须是聚合,这样替代对象的构造就不会遇到同样的问题。

因此,您可能正在考虑强制用户创建一个std::array(或一个语言数组)并传递它。您的函数可以采用某种形式的array_ref类,可以从任意大小的任何数组构造,因此消费函数不限于一种大小。

但是,您失去了大小的便利性:

foo( { "blah", 3, dog } );

对比

foo( std::array<bar, 3>{ "blah", 3, dog } );

避免此处冗长的唯一方法是将footakestd::array作为参数。这意味着它只能采用特定固定大小的数组。而且您不能使用 C++14 的建议dynarray,因为这将使用initializer_list中介。

最终,您不应该使用统一的初始化语法来传递值列表。它用于初始化对象,而不是用于传递事物列表。std::initializer_list是一个类,其唯一目的是用于从任意长的相同类型值列表中初始化特定对象。它在语言构造(一个花括号初始化列表)和这些值将被输入的构造函数之间充当中间对象。initializer_list它允许编译器在给定匹配的花括号初始化列表值时知道调用特定的构造函数(构造函数)。

这就是类存在的全部原因。

因此,您应该专门为设计它的目的使用该类。存在该类以将构造函数标记为从花括号初始化列表中获取值列表。因此,您应该仅将它用于采用此类值的构造函数。

如果您有一些函数foo充当某种内部类型(您不想直接公开)和用户提供的值列表之间的中介,那么您需要将其他东西作为foo. 具有您想要的语义的东西,然后您可以将其输入到您的内部类型中。

此外,您似乎对initializer_lists 和运动有误解。你不能搬出一个initializer_list但你当然可以搬进一个

foo( { "blah", 3, std::move(dog) } );

内部dog数组中的第三个条目将被移动构造。

于 2013-07-18T06:27:53.757 回答