若在下一步中对象被破坏,为什么不自动移动呢
Why not auto move if object is destroyed in next step?
如果函数返回如下值:
std::string foo() {
std::string ret {"Test"};
return ret;
}
编译器被允许移动ret,因为它不再被使用。这种情况不适用:
void foo (std::string str) {
// do sth. with str
}
int main() {
std::string a {"Test"};
foo(a);
}
虽然a
显然不再需要了,因为它在下一步中被销毁了,但你必须做:
int main() {
std::string a {"Test"};
foo(std::move(a));
}
为什么?在我看来,这是不必要的复杂,因为右值和移动语义很难理解,尤其是对于初学者来说。因此,如果您在标准情况下不必在意,但无论如何都可以从移动语义中受益(比如使用返回值和临时值),那就太好了。同样令人烦恼的是,必须查看类定义来发现一个类是否启用了移动并从std::move
中受益(或者无论如何都使用std::move
,希望它有时会有所帮助)
int main() {
std::string a {"Test"};
foo(std::move(a));
// [...] 100 lines of code
// new line:
foo(a); // Ups!
}
编译器更清楚一个对象是否不再被使用。无处不在的std::move
也很冗长,降低了可读性。
在给定点之后不使用对象并不明显。例如,看看您的代码的以下变体:
struct Bar {
~Bar() { std::cout << str.size() << std::endl; }
std::string& str;
}
Bar make_bar(std::string& str) {
return Bar{ str };
}
void foo (std::string str) {
// do sth. with str
}
int main() {
std::string a {"Test"};
Bar b = make_bar(a);
foo(std::move(a));
}
这段代码会中断,因为字符串a
被move操作置于无效状态,但Bar持有对它的引用,并将在它被销毁时尝试使用它,这种情况发生在foo
调用之后。
如果make_bar
是在外部程序集中定义的(例如DLL/so),则编译器在编译Bar b = make_bar(a);
时无法判断b是否持有对a
的引用。因此,即使foo(a)
是a
的最后一个用法,也不意味着使用移动语义是安全的,因为由于以前的指令,其他一些对象可能持有对a
的引用。
只有您才能通过查看所调用函数的规范来知道是否可以使用移动语义。
另一方面,在return
的情况下,您可以始终使用移动语义,因为该对象无论如何都会超出范围,这意味着任何引用它的对象都将导致未定义的行为,而不管移动语义如何。顺便说一句,由于复制省略,您甚至不需要将语义移动到那里。
这一切都是对"Destroyed"的定义的总结?std::string没有自毁的特殊效果,而是释放隐藏在里面的char数组。
如果我的析构函数做了什么特别的事情呢?例如,做一些重要的日志记录?然后通过简单地"移动它,因为它不再需要了",我错过了析构函数可能会做的一些特殊行为。
因为编译器不能进行更改程序行为的优化,除非标准允许。return
优化在某些情况下是允许的,但这种优化不允许用于方法调用。通过改变行为,它将跳过调用复制构造函数和析构函数,这可能会产生副作用(它们不需要是纯的),但通过跳过它们,这些副作用不会发生,因此行为将被改变。
(请注意,这在很大程度上取决于您试图传递的内容,在这种情况下,还取决于STL实现。在编译时所有代码都可用的情况下,编译器可能会确定复制构造函数和析构函数都是纯的,并对其进行优化。)
虽然编译器可以在第一个代码段中移动ret,但它也可以进行复制/移动省略,并将其直接构造到调用程序的堆栈中。这就是为什么不建议这样写函数:
std::string foo() {
auto ret = std::string("Test");
return std::move(ret);
}
现在对于第二个片段,您的字符串a是一个左值。Move语义仅适用于通过返回临时的、未命名的对象或强制转换左值而获得的右值引用。后者正是std::move所做的。
std::string GetString();
auto s = GetString();
// s is a lvalue, use std::move to cast it to rvalue-ref to force move semantics
foo(s);
// GetString returns a temporary object, which is a rvalue-ref and move semantics apply automatically
foo(GetString());
- 为什么不;名字在地图上是按顺序排列的吗
- 为什么不能修改对象中的值?另外,我如何改进此链表?
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- 当 std::move 与 C 样式数组或不移动对象时会发生什么
- C++ 基本 CTOR 说明 - 为什么不调用赋值/复制构造函数
- C++:为什么不调用移动构造函数?
- 为什么不调用移动构造函数
- 我可以从向量的开头移动对象吗?为什么不呢
- 为什么函数返回不移动向量?
- 为什么C++默认情况下不移动构造右值引用?
- 为什么当我改变光源的位置时,光源不移动?
- 为什么当我不移动任何东西时,clang 会抱怨删除移动 ctor?
- 为什么可以移动不可用的lambda捕获
- OpenGL - 为什么我的球不移动?
- 为什么不调用移动构造函数?
- 若在下一步中对象被破坏,为什么不自动移动呢
- 移动语义,为什么不调用移动构造函数
- 为什么这个移动构造函数不起作用
- 为什么不调用移动构造函数?
- 右值参考:为什么不隐式移动右值?