对象传递给STD ::移动,但不会移动
Object passed to std::move but not moved from?
我正在审查一些这样的代码,其中 A
是一种可移动类型:
// Returns true exactly when ownership of a is taken
bool MaybeConsume(A&& a) {
if (some condition) {
Consume(std::move(a)); // ???
return true;
}
return false;
}
// ... elsewhere ...
A a;
if (!MaybeConsume(std::move(a))) {
a.DoSomething(); // !!!
}
我们的静态分析工具抱怨被移动后使用a
(在!!!
(。IIUC std::move
只是static_cast
,直到调用移动构造函数或分配运算符(大概在Consume
中(之前,对象a
实际上不会被弄脏。假设MaybeConsume
在评论中满足合同,
- 这有效吗?
- 是ub?
-
std::move
是???
a no-op?
(可能可以重构这个特定的实例以避免微妙,但我仍然想寻求自己的理解(。
这是您静态分析工具的虚假警告。
- 这有效吗?
是的,MaybeConsume
正在做评论所说的话。当some condition
为True时,它仅具有其参数的所有权(假设Consume
实际上确实从其参数中移动构造/分配(。
std::move
确实只是一个花哨的static_cast<T&&>
,因此MaybeConsume(std::move(a))
不是转移所有权,您只是将引用绑定到MaybeConsume
的参数。
- 是ub?
否,如果MaybeConsume
表示它已承担其参数所有权,则您不使用a
。
std::move
是 ??? a no-op?
好吧,这是一个无关,因为它只是static_cast
,但是如果您打算问它是否不必要,那么,不是,不是。在MaybeConsume
的主体中,a
是一个LVALUE,因为它具有名称。如果Consume
的签名是void Consume(A&&)
,则没有std::move
。
从您显示的示例用法中,看来您不应该用prvalue参数调用 MaybeConsume
,因为如果函数返回false
,呼叫者大概应该以其他方式使用该参数。如果是这样,那么您应该将其签名更改为bool MaybeConsume(A&)
。这可能会使您的静态分析工具感到高兴,因为这允许您编写if (!MaybeConsume(a))
。
要了解为什么静态分析工具发出警告,需要以静态分析仪的方式思考。当看到一块像以下的代码时:
A a;
fun(std::move(a);
a.method();
尚不清楚fun((呼叫内部可能会发生什么。在A上成功执行方法((取决于某些先决条件的满足,这可能(或不再(在fun of Fun((之后不再存在。虽然程序员很可能知道可以安全地调用方法((,但分析仪不会发出警告。
以下只是我自己的看法。假设A的所有权完全由Fun((完全占据。为了防止混乱,最好要执行借来返回的风格,就好像一个朋友从您那里借了一本书一样,您(不能(在返回之前(无法(使用该书。因此,永远不要冒险自己会意外援引那时应该"死"的对象。
请参阅以下演示代码:
#include <iostream>
#include <utility>
#include <tuple>
#include<cassert>
struct A {
public:
int *p;
public:
A() {
p = new int();
assert(p != nullptr);
std::cout << p << std::endl;
std::cout << "default constrctor is called" << std::endl;
}
A(const A&) = delete;
A& operator=(const A&) = delete;
A(A&& _a): p(_a.p) {
_a.p = nullptr;
std::cout << p << std::endl;
std::cout << "move constructor is called" << std::endl;;
}
A& operator=(A&& _a) {
std::cout << "move assignment is called"<<std::endl;;
p = std::move(_a.p);
return *this;
}
void DoSomthing(){
std::cout << "do somthing is called" << std::endl;
*p = 100;
std::cout << "value of p is changed"<<std::endl;
}
};
std::tuple<A&&, bool> MaybeConsume(A&& a) {
if (1==2) {//try 1==1 alternatively
delete a.p;
a.p = nullptr;//consume
std::cout << "content consumed" << std::endl;
return std::make_tuple(Consume(std::move(a)), true);
}
else {
return std::make_tuple(std::move(a), false);
}
}
int main()
{
A a;
std::tuple<A&&, bool> t = MaybeConsume(std::move(a));
if (!(std::get<bool> (t))) {
A a1 = std::move(std::get<A&&>(t));
a1.DoSomthing();
}
return 0;
}
- 仅包含可移动 std::map 的类的移动构造函数不起作用
- 如何在窗口之间移动 std::unique_ptr 而不会冒内存泄漏的风险?
- 为什么移动 std::可选不重置状态
- C++:我可以重用/移动 std::list 元素从中间到结尾吗?
- 我应该移动 std::exchange ed 成员吗?
- 移动 std::bitset<N> 是否超过 N 个位置未定义的行为?
- 标准是否保证在移动std::p ackaged_task后安全使用std::future?
- 用于移动 std::shared_ptr 的函数声明
- 如何移动 std::ostringstream 的底层字符串对象?
- 移动std::shared_ptr会使程序崩溃
- c++11移动std::deque或std::list的插入
- 如何移动std::ostream
- 移动std::bind和std::thread中的语义/行为
- 为什么我不能移动 std::ofstream?
- 从char*中移动std::string的构造函数
- 移动 std::vector 成员的语义
- 移动std::unique_locks的std::矢量
- 复制/移动std::映射中键/值类型的要求
- 移动std::互斥锁的构造函数
- 在容器之间移动std::unique_ptr