繁体   English   中英

对象传递给std :: move但是没有从中移动?

[英]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在评论中满足合同,

  1. 这有用吗?
  2. 是UB吗?
  3. std::move??? 没有操作?

(可能这个特殊的实例可以重构以避免微妙,但我仍然想要求我自己的理解)。

这是静态分析工具的虚假警告。

  1. 这有用吗?

是的, MaybeConsume正在做评论所说的。 some condition为真时,它只接受其参数的所有权(假设Consume实际上确实从其参数移动了构造/赋值)。

std::move确实只是一个花哨的static_cast<T&&>所以MaybeConsume(std::move(a))没有转移所有权,你只是绑定对MaybeConsume参数的引用。

  1. 是UB吗?

不,你没有利用的a ,如果MaybeConsume表示它已承担其参数的所有权。

  1. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM