[英]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
,并且在调用移动构造函数或赋值运算符之前,对象a
实际上不会被删除(可能是在Consume
)。 假设MaybeConsume
在评论中满足合同,
std::move
在???
没有操作? (可能这个特殊的实例可以重构以避免微妙,但我仍然想要求我自己的理解)。
这是静态分析工具的虚假警告。
- 这有用吗?
是的, MaybeConsume
正在做评论所说的。 当some condition
为真时,它只接受其参数的所有权(假设Consume
实际上确实从其参数移动了构造/赋值)。
std::move
确实只是一个花哨的static_cast<T&&>
所以MaybeConsume(std::move(a))
没有转移所有权,你只是绑定对MaybeConsume
参数的引用。
- 是UB吗?
不,你没有利用的a
,如果MaybeConsume
表示它已承担其参数的所有权。
- 是
std::move
在??? 没有操作?
嗯,这是一个无操作,因为它只是一个static_cast
,但如果你想问这是否是不必要的,那么,不,它不是。 在MaybeConsume
的主体内, a
是一个左值, 因为它有一个名字 。 如果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上成功执行method()取决于满足某些先决条件,在调用fun()之后可能不会(或不再)保持。 虽然程序员可能知道调用method()是安全的,但分析器却没有,因此它会发出警告。
以下只是我自己的看法。 假设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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.