默认移动构造
Default move construction
《C++编程语言》一书中有一个关于默认操作的例子。我的问题集中在默认移动操作上。
struct S {
std::string a;
int b;
};
S f(S arg) {
S s0{};
S s1(s0); //s1 {s0}; in the book
s1 = arg;
return s1;
}
之后它说:
s1的复制构造复制s0.a和s0.b。s1的返回移动s1.a和s1.b,使s1.a为空字符串,s1.b保持不变。请注意,内置类型的moved-from对象的值是不变的。这是编译器要做的最简单、最快的事情。
我想这意味着如果我写:
S s3{"tool",42};
f(s3);
由于s1的值被移动,s1.a将返回到",而s1.b不变?那么当f()执行完毕,s1会被销毁吗?我正试图找到一种方法来测试我的猜测,但在函数执行后,我找不到知道s1值的方法,因为它当然是本地的。我写了一个析构函数,只是为了在值被破坏之前找到它们。
~S() {
std::cout << a << " " << b << 'n';
}
输出为:
0 //values of s0?
tool 42
tool 42
tool 42
看来我的猜测完全错了,我完全不懂课文。有人能更清楚地解释引文中的文字吗?
首先,通过定义析构函数,您禁用了隐式移动构造函数。你必须添加到你的代码:
S(const S&) = default;
S(S&&) = default;
S& operator=(const S&) = default;
S& operator=(S&&) = default; // not required here but should be added for completeness
然后,无论如何,RVO开始发挥作用。正如其他答案中所指出的,编译器可以省略对复制和移动构造函数的调用。在GCC和clang中,您可以通过添加-fno-elide-constructors
编译器选项来禁用此功能。之后你会得到这个输出:
42 // moved s1 (this can theoretically be different, because the value of s1.a is unspecified)
0 // s0
tool 42 // arg
tool 42 // return value of f()
tool 42 // s3
演示
s1.a
将返回""
也许吧。根据move构造函数的规范,它"处于具有未指定值的有效状态"。通常,如果字符串使用动态分配的数组,并且两个字符串都使用兼容的分配器,则move将转移数组的所有权,使旧字符串为空。但是,如果字符串使用"短字符串优化",即短字符串存储在string
对象内部以节省动态内存分配的成本,那么保持旧字符串不变会更快。
并且CCD_ 5是不变的
是的。从指定时,基本体类型永远不会修改。
那么当
f()
执行完毕时,s1
会被销毁吗?
可能不会。除非你有一个非常原始的编译器或故意禁用优化,否则这里可能会使用移动省略的优化(有时称为"返回值优化")。s1
将在调用方的堆栈帧中创建,因此无需返回它
在函数执行后,我找不到知道s1值的方法
不,返回后无法检查对象,因为它已不存在。如果你想看看移动的效果,你可以移动到一个新的局部变量
S s2 = std::move(s1);
然后检查CCD_ 9。或者你可以编写自己的移动构造函数
S(S && other) : a(std::move(other.a)), b(b) {
std::cout << other.a << 'n';
}
但是,如上所述,这可能不会用于函数返回值。
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- 基类中的默认析构函数禁用子类中的移动构造函数(如果有成员)
- 使用移动调用对等构造函数unique_ptr默认构造函数
- 为什么 std::memmove 中联合的默认非平凡移动构造函数C++?
- 我什么时候会默认(而不是删除)基类中的复制和移动操作
- 这将执行默认移动操作吗?
- 如何对移动操作进行单元测试(默认)?
- 为什么 std::move 不将默认移动构造函数中的源变量更改为默认值?
- 为什么C++默认情况下不移动构造右值引用?
- 移动构造函数中的默认参数
- 默认复制/移动构造函数时 GDB 中的奇怪行为
- 默认移动成员定义为已删除,而未定义特殊成员?
- 删除复制构造函数是否也会删除默认的复制/移动运算符?
- 是否可以从移动构造函数调用默认构造函数?
- C++ 默认构造函数在移动和复制构造函数存在时不随"using"继承
- 显式默认析构函数禁用类中的默认移动构造函数
- 编译器是否优化了默认移动构造函数?
- C2280 STD :: vector的默认构造函数尽管具有移动构造函数,但仍需要元素的复制构造函数
- POD 类型是否可以具有显式声明的默认移动分配运算符
- 具有互斥锁成员的默认移动构造函数