为了账号安全,请及时绑定邮箱和手机立即绑定

初始化器列表和移动语义

初始化器列表和移动语义

C++
慕尼黑的夜晚无繁华 2019-07-13 14:58:16
初始化器列表和移动语义是否允许我将元素移出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+标准库的普通容器那样的值语义,我宁愿是安全的,也不愿道歉和询问。
查看完整描述

3 回答

?
慕的地10843

TA贡献1785条经验 获得超8个赞

不,这不像预期的那样,你还是会得到副本的。我对此相当惊讶,因为我以为initializer_list存在是为了保存一组临时人员,直到他们move德。

beginendinitializer_list回归const T *的结果move在您的代码中是T const &&-不变的参考价值。这样的表达是没有意义的。它将绑定到类型为的函数参数。T const &因为rvalue确实绑定到Constlvalue引用,所以您仍然会看到复制语义。

这可能是因为编译器可以选择将initializer_list静态初始化的常量,但似乎使其类型更加清晰。initializer_listconst initializer_list由编译器自行决定,因此用户不知道是否需要const或易变的结果beginend..但这只是我的直觉,可能有一个很好的理由我错了。

最新情况:我写过ISO提案initializer_list只支持移动类型。它只是第一个草案,还没有在任何地方实现,但是您可以看到它来对问题进行更多的分析。


查看完整回答
反对 回复 2019-07-13
?
隔江千里

TA贡献1906条经验 获得超10个赞

bar(std::move(*it));   // kosher?

不是以你想要的方式。您不能移动const对象。和std::initializer_list只提供const进入它的元素。所以itconst T *.

你想打电话std::move(*it)只会产生l值。艾莉:一份。

std::initializer_list参考文献静态记忆。这就是上课的目的。你不能移动因为运动意味着改变它。你只能复制它。


查看完整回答
反对 回复 2019-07-13
?
临摹微笑

TA贡献1982条经验 获得超2个赞

这不像前面说的那样,因为list.begin()有型const T *,你不可能从一个常量物体上移动。语言设计人员可能是这样做的,这样做是为了允许初始化程序列表包含字符串常量,从这些常量中移动是不合适的。

但是,如果您知道初始化程序列表包含rvalue表达式(或者您想强迫用户编写这些表达式),那么就有一个技巧可以让它工作(我被Sumant的答案所启发,但解决方案比那个简单得多)。您需要存储在初始化程序列表中的元素不是T值,但是封装了T&&..即使这些价值观本身const合格后,它们仍然可以检索可修改的rvalue。

template<typename T>
  class rref_capture{
  T* ptr;public:
  rref_capture(T&& x) : ptr(&x) {}
  operator T&& () const { return std::move(*ptr); } // restitute rvalue ref};

而不是声明initializer_list<T>参数,则声明initializer_list<rref_capture<T> >争论。下面是一个具体的例子,涉及到std::unique_ptr<int>智能指针,只为其定义了移动语义(因此这些对象本身永远不能存储在初始化程序列表中);但是下面的初始化程序列表没有问题地编译。

#include <memory>#include <initializer_list>class uptr_vec{
  typedef std::unique_ptr<int> uptr; // move only type
  std::vector<uptr> data;public:
  uptr_vec(uptr_vec&& v) : data(std::move(v.data)) {}
  uptr_vec(std::initializer_list<rref_capture<uptr> > l)
    : data(l.begin(),l.end())
  {}
  uptr_vec& operator=(const uptr_vec&) = delete;
  int operator[] (size_t index) const { return *data[index]; }};int main(){
  std::unique_ptr<int> a(new int(3)), b(new int(1)),c(new int(4));
  uptr_vec v { std::move(a), std::move(b), std::move(c) };
  std::cout << v[0] << "," << v[1] << "," << v[2] << std::endl;}

有一个问题需要回答:如果初始化程序列表中的元素应该是真prvalue(在示例中它们是xvalue),那么该语言是否确保相应的临时程序的生存期延长到使用它们的位置?坦率地说,我认为标准的相关第8.5节根本没有涉及这个问题。然而,阅读1.9:10,似乎是相关的全表达在所有情况下,都包括初始化程序列表的使用,因此我认为不存在悬挂rvalue引用的危险。


查看完整回答
反对 回复 2019-07-13
  • 3 回答
  • 0 关注
  • 470 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信