代码之家  ›  专栏  ›  技术社区  ›  t.niese

“移动后使用[移动后易出错使用]”是否警告此处存在真正的问题?

  •  3
  • t.niese  · 技术社区  · 7 年前

    我正在修改CPPCon2016中讨论的失效的PTR Herb Sutter的想法,以便能够以更安全的方式管理以ID表示的外部资源。

    因此,我创建了一个不可复制且只能移动的类,其中包含 id 资源应该表示。像一个 unique_ptr 这个 身份证件 应该成为 0 如果对象移动到另一个对象。

    据我所知,即使在被调用函数没有任何前提条件的情况下,也应该允许您使用对象,因此据我所知,这应该是有效的:

    int main() {
    
        resource src = make_resource(10);
        resource dst;
        std::cout << "src " << src.get() << std::endl;
        std::cout << "dst " << dst.get() << std::endl;
    
        dst = std::move(src);
        std::cout << "src " << src.get() << std::endl; // (*)
        std::cout << "dst " << dst.get() << std::endl;
    
        src = make_resource(40);
        std::cout << "src " << src.get() << std::endl;
        std::cout << "dst " << dst.get() << std::endl;
    
        return 0;
    }
    

    但是 clang-tidy 给我这个警告:

    警告:在移动后使用了“src”[移动后易出错使用]

    对于 src.get() dst = std::move(src) (上面标明)。

    所以我的问题是:

    1. 我可以打电话吗 SRC std::move(src)
    2. 我可以假设一下 SRC 收益率 std::move .
    3. 如果 1。 2。 是有效的,那么有没有一种方法可以更改代码,以便家族整理人员知道这是有效的。如果没有,是否有办法改变代码,使其有效?

    下面是类的实现:

    struct resource {
        resource() = default;
    
        // no two objects are allowed to have the same id (prevent double free, only 0 is allowed multiple times as it represents nullptr)
        resource(const resource&) = delete;
        resource& operator=(const resource& other) = delete;
    
        // set the id of the object we move from back to 0 (prevent double free)
        resource(resource&& other) noexcept : id(std::exchange(other.id, 0)) {}
        resource& operator=(resource&& other) noexcept {
            id = std::exchange(other.id, 0);
            return *this;
        }
    
        // will free the external resource if id not 0
        ~resource() = default;
    
        // returns the id representing the external resource
        int get() const noexcept { return id; }
    
      protected:
        // only allow the make function to call the constructor with an id
        friend resource make_resource(int id);
        explicit resource(int id) : id(id) {}
    
      protected:
        int id = 0; // 0 = no resource referenced
    };
    
    // in the final version the id should be retrieved by from the external ip
    resource make_resource(int id) { return std::move(resource(id)); }
    
    2 回复  |  直到 7 年前
        1
  •  5
  •   eerorika    7 年前
    1. 允许我在std::move(src)之后调用src.get()吗?

    如果我们对 src 我们不知道。可能不会。在某些情况下,移动后调用成员函数是未定义的。例如,调用智能指针的间接运算符。

    给出了 decltype(src) 它的成员函数,你已经展示了,我们知道:是的,你可以。

    1. 我可以假设在std::move之后src.get()返回0吗?

    如果我们对 SRC 我们什么都不知道 src.get() 做。更具体地说,我们不知道它的前提条件。

    给出了 解密(SRC) 它的成员函数,如您所示:是的,我们可以做假设。

    1. 如果是1。2。是有效的,那么有没有一种方法可以更改代码,以便家族整理人员知道这是有效的。如果没有,是否有办法改变代码,使其有效?

    Clang Tidy假定“不处于移出状态”是所有成员功能(而不是分配)的前提条件,在此假设下,警告违反了这种假定的前提条件。因此,它试图强制执行一个惯例,即总是假定这样的成见,即使您碰巧知道它不存在于您的类中。

    您可以取消对的呼叫 SRC 在移动和重新分配 SRC . 对clang tidy不抱怨的moved-from变量的一个操作是重新分配,在分配之后,对象的状态(通常)应该被很好地定义,并且调用其他成员函数被假定为良好(当然,您可以有其他必须满足的前提条件,但是clang tidy可能不知道哼哼) 虽然从技术上讲,可以定义一个类型,即使移动后的分配也没有很好地定义,但这种类型将非常不传统和不安全。


    最后,你 可以 呼叫 SRC 在移动之后(甚至在重新分配之前),但是您将不会遵循ClangTidy试图强制执行的约定。

        2
  •  5
  •   Baum mit Augen    7 年前

    cppreference.com已经 this text :

    除非另有规定,否则 “已从移动”处于有效但未指定的状态。也就是说, 只有不带前提条件的函数,如赋值 操作员,可在物体从以下位置移动后安全使用:

    所以,非正式地,C++ 惯例 这就是为什么ClangTidy暗示使用它是可疑的。

    对于您的实现,您提供的不仅仅是约定——所以您的代码没有错,只是非常规。