initializer_list和移动语义

fre*_*low 88 c++ templates initializer-list move-semantics c++11

我允许将元素移出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?
    }
}
Run Code Online (Sandbox Code Playgroud)

由于std::intializer_list<T>需要特殊的编译器注意并且没有像C++标准库的普通容器那样的值语义,所以我宁愿安全而不是抱歉并且问.

Pot*_*ter 82

不,这不会按预期工作; 你仍然会得到副本.我对此感到非常惊讶,因为我认为initializer_list存在一系列的临时工,直到他们为止move.

begin并且end对于initializer_listreturn const T *,所以move代码中的结果是T const &&- 一个不可变的右值引用.这样的表达无法有意义地被移除.它将绑定到类型的函数参数,T const &因为rvalues绑定到const左值引用,您仍将看到复制语义.

原因可能是编译器可以选择initializer_list使用静态初始化的常量,但是看起来它的类型initializer_listconst initializer_list编译器可以自行决定是否更清晰,因此用户不知道是期望const还是可变的结果来自beginend.但这只是我的直觉,可能有一个很好的理由我错了.

更新:我已经编写了ISO提议,initializer_list支持仅移动类型.它只是初稿,并没有在任何地方实现,但你可以看到它来更多地分析问题.

  • 如果不清楚,它仍然意味着使用`std :: move`是安全的,如果没有生产力.(除非`T const &&`移动构造函数.) (10认同)
  • @Potatoswatter,迟到的评论,但提案的状态是什么。它是否有可能使其成为 C++20? (2认同)

Nic*_*las 17

bar(std::move(*it));   // kosher?
Run Code Online (Sandbox Code Playgroud)

不是你想要的方式.您无法移动const对象.并且std::initializer_list只提供const对其元素的访问.所以类型itconst T *.

您的通话尝试std::move(*it)只会产生一个l值.IE:副本.

std::initializer_list引用静态内存.这就是班级的用途.你无法从静态内存中移动,因为移动意味着要改变它.您只能从中复制.

  • @Potatoswatter:你不能从一个常量对象移动.`initializer_list`对象本身可能是一个xvalue,但它的内容(它指向的实际值数组)是`const`,因为这些内容可能是静态值.你根本无法从`initializer_list`的内容移动. (4认同)