为什么要编译以下代码?
#include <vector>
#include <iostream>
struct Foo {
std::vector<int> bar = {1, 2, 3};
};
int main()
{
Foo foo1;
const Foo& foo2 = foo1;
std::vector<int> target;
std::move(foo2.bar.begin(), foo2.bar.end(), std::back_inserter(target));
return 0;
}
std::move的文档显示
执行此操作后,移动自范围中的元素仍将包含适当类型的有效值,但不一定是与移动前的值相同。
所以这实际上可以更改对象foo2,即使它被声明为const。为什么这样做?
所以这实际上可以更改对象foo2,即使它被声明为const。为什么这样做?
允许std::move
算法移动输入元素,如果可以的话。
对于每个输入元素,它执行*dest = std::move(*from)
,其中dest
和from
是输出迭代器和输入迭代器。由于from
取消了对常量对象的引用,std::move(*from)
创建了一个右值引用const int&&
。由于int
没有用户定义的构造函数,因此对*dest
的赋值实际上会导致由该语言定义的复制构造。
如果元素是具有用户定义的复制和移动构造函数的类类型T
,则重载解析将不得不选择复制构造函数(T(const T&)
(而不是移动构造函数(T(T&&)
(,因为const
左值引用可以绑定到const
右值,而非const
右值引用不能(因为这需要丢弃const
(。
底线是std::move
(带迭代器的算法(正在执行移动操作,该操作可能调用也可能不调用移动构造函数或赋值。如果调用了move构造函数或赋值,并且该move对源具有破坏性,则算法将修改源元素。在其他情况下,它只会执行一次复制。
要用例子演示Andrey Semashev的答案,请考虑以下内容:
#include <vector>
struct movable
{
movable() = default;
movable(const movable&) = delete;
movable& operator=(const movable&) = delete;
movable(movable&&) = default;
movable& operator=(movable&&) = default;
};
struct copyable
{
copyable() = default;
copyable(const copyable&) = default;
copyable& operator=(const copyable&) = default;
copyable(copyable&&) = delete;
copyable& operator=(copyable&&) = delete;
};
int main()
{
// original example
const std::vector<int> si;
std::vector<int> ti;
std::move(si.begin(), si.end(), std::back_inserter(ti)); // OK
// example 2
const std::vector<copyable> sc;
std::vector<copyable> tc;
std::move(sc.begin(), sc.end(), std::back_inserter(tc)); // OK
// example 3
const std::vector<movable> sv;
std::vector<movable> tv;
std::move(sv.begin(), sv.end(), std::back_inserter(tv)); // ERROR - tries to use copy ctor
return 0;
}
尽管copyable
没有移动构造函数,但示例2编译时没有错误,因为std::move
在这里选择了复制构造函数。
另一方面,示例3未能编译,因为movable
的move构造函数被sv
的常量否定(更好的词?(。你得到的错误是:
error: use of deleted function 'movable::movable(const movable&)'
下面是一个完整的例子。
更新
分步说明:
由于我们的类型是
const std::vector<T>
,其vector::begin()
函数返回const_iterator
。当
const_iterator
在std::move
算法内被取消引用时,返回const T&
。std::move
算法内部使用std::move
函数,例如:// taken from cppreference.com while (first != last) *d_first++ = std::move(*first++);
std::move
函数依次返回:// taken from cppreference.com static_cast<typename std::remove_reference<T>::type&&>(t)`
因此,对于
const T&
,它返回const T&&
。由于我们没有用
const T&&
参数定义构造函数或operator=
,因此重载解析会选择使用const T&
的构造函数。沃伊拉。