"它被移动后使用[bugprone-use-after-move]"是否在这里警告了一个真正的问题?

t.n*_*ese 4 c++ move c++17 clang-tidy

我适应递延PTR香草萨特在cppcon 2016谈及的理念,能够管理在一个更安全的方式一个id为代表的外部资源.

因此,我创建了一个非复制的,只有可移动的类保存id资源应该代表.像unique_ptrid应该成为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;
}
Run Code Online (Sandbox Code Playgroud)

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

警告:'src'被移动后使用[bugprone-use-after-move]

对于src.get()之后dst = std::move(src)(标记为上文).

所以我的问题是:

  1. 我被允许打电话src.get()std::move(src)
  2. 我可以假设src.get()回归0之后std::move.
  3. 如果1.2. 有效,那么有没有办法更改代码,以便clan-tidy知道这是有效的.如果没有,有没有办法改变它有效的代码?

这是该类的实现:

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)); }
Run Code Online (Sandbox Code Playgroud)

eer*_*ika 7

  1. 我可以在std :: move(src)之后调用src.get()

如果我们对类型保持不可知src,那么我们不知道.可能不是.有些情况下,移动后调用成员函数将是未定义的.例如,调用智能指针的间接运算符.

鉴于您展示的定义decltype(src)及其成员函数,我们知道:是的,您被允许.

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

如果我们对这种类型保持不可知src,那么我们什么都不知道src.get().更具体地说,我们不知道它的先决条件.

鉴于decltype(src)您已经显示的定义及其成员函数:是的,我们可以做出假设.

  1. 如果1.和2.有效,那么有没有办法更改代码,以便clan-tidy知道这是有效的.如果没有,有没有办法改变它有效的代码?

Clang-tidy假设"不是从状态移动"是所有成员函数(除了赋值)的先决条件,并且在该假设下,警告这样的假设前提条件被违反.因此,它试图强制执行一个约定,即使你碰巧知道它不存在于你的类中,也总是假设这样的预先存在.

您可以删除src.get()移动和重新分配之间的调用src.从clang-tidy没有抱怨的变量移动的一个操作是重新赋值,并且在该赋值之后,对象的状态应该(通常)被很好地定义并且调用其他成员函数被认为是好的(当然,你可以有其他必须满足的前提条件,但铿锵可能不知道它们).虽然从技术上讲,有可能定义一种类型,即使移动后的分配没有很好地定义,但这种类型将是非常传统和不安全的.


最后,你可以调用src.get()移动(甚至重新分配前)这个特殊的下课,但后来你会不会是铛,整洁正在努力执行该公约以下.


Pet*_*ull 6

cppreference.com有这样的文字:

除非另行指定,否则已移出的所有标准库对象都将处于有效但未指定的状态.也就是说,只有没有前置条件的函数(如赋值运算符)才能在对象移动后安全地使用:

因此,非正式的,C++的惯例是移动的,从对象将是有效的,但没用,这就是为什么铛,整洁是在暗示这是可疑的被使用.

对于您的实现,您提供了"更多"而不是约定 - 因此您的代码没有错,只是非常规.

  • @LightnessRacesinOrbit“非正式约定”可能不是正确的术语,但我的意思是 - IMO,以让理解 C++ 标准库的人感到惊讶的方式设计事物是不明智的,即使 C++ 语言不能强制执行。举一个愚蠢的例子,除了约定之外,没有什么可以阻止你创建一个容器类,其中的方法“begin()”删除内容。 (2认同)