初始化器列表和移动语义

初始化器列表和移动语义

是否允许我将元素移出std::initializer_list<T>?

#include <initializer_list>#include <utility>template<typename T>void foo(std::initializer_list<T> list){
    for (auto it = list.begin(); it != list.end(); ++it)
    {
        bar(std::move(*it));   // kosher?
    }}

std::intializer_list<T>需要特别的编译器注意,并且没有像C+标准库的普通容器那样的值语义,我宁愿是安全的,也不愿道歉和询问。


慕尼黑的夜晚无繁华
浏览 471回答 3
3回答

慕的地10843

不,这不像预期的那样,你还是会得到副本的。我对此相当惊讶,因为我以为initializer_list存在是为了保存一组临时人员,直到他们move德。begin和end为initializer_list回归const T *的结果move在您的代码中是T const &&-不变的参考价值。这样的表达是没有意义的。它将绑定到类型为的函数参数。T const &因为rvalue确实绑定到Constlvalue引用,所以您仍然会看到复制语义。这可能是因为编译器可以选择将initializer_list静态初始化的常量,但似乎使其类型更加清晰。initializer_list或const initializer_list由编译器自行决定,因此用户不知道是否需要const或易变的结果begin和end..但这只是我的直觉,可能有一个很好的理由我错了。最新情况:我写过ISO提案为initializer_list只支持移动类型。它只是第一个草案,还没有在任何地方实现,但是您可以看到它来对问题进行更多的分析。

隔江千里

bar(std::move(*it));&nbsp;&nbsp;&nbsp;//&nbsp;kosher?不是以你想要的方式。您不能移动const对象。和std::initializer_list只提供const进入它的元素。所以it是const T *.你想打电话std::move(*it)只会产生l值。艾莉:一份。std::initializer_list参考文献静态记忆。这就是上课的目的。你不能移动因为运动意味着改变它。你只能复制它。

临摹微笑

这不像前面说的那样,因为list.begin()有型const T *,你不可能从一个常量物体上移动。语言设计人员可能是这样做的,这样做是为了允许初始化程序列表包含字符串常量,从这些常量中移动是不合适的。但是,如果您知道初始化程序列表包含rvalue表达式(或者您想强迫用户编写这些表达式),那么就有一个技巧可以让它工作(我被Sumant的答案所启发,但解决方案比那个简单得多)。您需要存储在初始化程序列表中的元素不是T值,但是封装了T&&..即使这些价值观本身const合格后,它们仍然可以检索可修改的rvalue。template<typename&nbsp;T> &nbsp;&nbsp;class&nbsp;rref_capture{ &nbsp;&nbsp;T*&nbsp;ptr;public: &nbsp;&nbsp;rref_capture(T&&&nbsp;x)&nbsp;:&nbsp;ptr(&x)&nbsp;{} &nbsp;&nbsp;operator&nbsp;T&&&nbsp;()&nbsp;const&nbsp;{&nbsp;return&nbsp;std::move(*ptr);&nbsp;}&nbsp;//&nbsp;restitute&nbsp;rvalue&nbsp;ref};而不是声明initializer_list<T>参数,则声明initializer_list<rref_capture<T> >争论。下面是一个具体的例子,涉及到std::unique_ptr<int>智能指针,只为其定义了移动语义(因此这些对象本身永远不能存储在初始化程序列表中);但是下面的初始化程序列表没有问题地编译。#include&nbsp;<memory>#include&nbsp;<initializer_list>class&nbsp;uptr_vec{ &nbsp;&nbsp;typedef&nbsp;std::unique_ptr<int>&nbsp;uptr;&nbsp;//&nbsp;move&nbsp;only&nbsp;type &nbsp;&nbsp;std::vector<uptr>&nbsp;data;public: &nbsp;&nbsp;uptr_vec(uptr_vec&&&nbsp;v)&nbsp;:&nbsp;data(std::move(v.data))&nbsp;{} &nbsp;&nbsp;uptr_vec(std::initializer_list<rref_capture<uptr>&nbsp;>&nbsp;l) &nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;data(l.begin(),l.end()) &nbsp;&nbsp;{} &nbsp;&nbsp;uptr_vec&&nbsp;operator=(const&nbsp;uptr_vec&)&nbsp;=&nbsp;delete; &nbsp;&nbsp;int&nbsp;operator[]&nbsp;(size_t&nbsp;index)&nbsp;const&nbsp;{&nbsp;return&nbsp;*data[index];&nbsp;}};int&nbsp;main(){ &nbsp;&nbsp;std::unique_ptr<int>&nbsp;a(new&nbsp;int(3)),&nbsp;b(new&nbsp;int(1)),c(new&nbsp;int(4)); &nbsp;&nbsp;uptr_vec&nbsp;v&nbsp;{&nbsp;std::move(a),&nbsp;std::move(b),&nbsp;std::move(c)&nbsp;}; &nbsp;&nbsp;std::cout&nbsp;<<&nbsp;v[0]&nbsp;<<&nbsp;","&nbsp;<<&nbsp;v[1]&nbsp;<<&nbsp;","&nbsp;<<&nbsp;v[2]&nbsp;<<&nbsp;std::endl;}有一个问题需要回答:如果初始化程序列表中的元素应该是真prvalue(在示例中它们是xvalue),那么该语言是否确保相应的临时程序的生存期延长到使用它们的位置?坦率地说,我认为标准的相关第8.5节根本没有涉及这个问题。然而,阅读1.9:10,似乎是相关的全表达在所有情况下,都包括初始化程序列表的使用,因此我认为不存在悬挂rvalue引用的危险。
打开App,查看更多内容
随时随地看视频慕课网APP