use*_*931 20 c++ language-lawyer move-semantics
为什么下面的代码可以编译通过?
#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;
}
Run Code Online (Sandbox Code Playgroud)
std::move的文档说
在此操作之后,移出范围中的元素仍将包含适当类型的有效值,但不一定与移动之前的值相同。
所以这实际上可以改变对象 foo2,即使它被声明为 const。为什么这有效?
And*_*hev 27
所以这实际上可以改变对象 foo2,即使它被声明为 const。为什么这有效?
如果可以的话,允许std::move算法移动输入元素。
对于每个输入元素,它执行*dest = std::move(*from),其中dest和from是输出和输入迭代器。由于from取消引用常量对象,std::move(*from)因此创建右值引用const int&&。由于ints 没有用户定义的构造函数,因此对 的赋值*dest实际上会产生由该语言定义的复制构造。
如果您的元素属于具有用户定义的复制和移动构造函数的类类型T,则重载解析必须选择复制构造函数 ( T(const T&)) 而不是移动构造函数 ( T(T&&)),因为const左值引用可以绑定到const右值,而非const右值引用可以绑定到右值。 t (因为这需要抛弃const)。
底线是std::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;
}
Run Code Online (Sandbox Code Playgroud)
即使copyable没有移动构造函数,示例 2 编译时也不会出现错误,因为std::move此处选择了复制构造函数。
另一方面,示例 3 无法编译,因为 的移动构造函数movable被 的常量性否定(更好的词?)sv。您得到的错误是:
error: use of deleted function 'movable::movable(const movable&)'
Run Code Online (Sandbox Code Playgroud)
这是一个完整的例子。
更新
分步说明:
由于我们的类型是const std::vector<T>,所以它的vector::begin()函数返回const_iterator。
const_iteratorstd::move当在算法内部取消引用时,返回const T&。
std::movestd::move算法内部使用函数,例如:
error: use of deleted function 'movable::movable(const movable&)'
Run Code Online (Sandbox Code Playgroud)
std::move函数依次返回:
// taken from cppreference.com
while (first != last) *d_first++ = std::move(*first++);
Run Code Online (Sandbox Code Playgroud)
所以,因为const T&它返回const T&&。
由于我们没有构造函数或operator=使用const T&&参数定义,因此重载解析会选择需要的构造函数const T&。
瞧。
| 归档时间: |
|
| 查看次数: |
2102 次 |
| 最近记录: |