"used after it was moved [bugprone-use-after-move]" 警告是一个真正的问题吗?
Is the "used after it was moved [bugprone-use-after-move]" warning a real problem here?
我正在调整 Herb Sutter 在 cppcon 2016 中谈到的延迟 ptr 的想法,以便能够以更安全的方式管理由 id 表示的外部资源。
因此,我创建了一个不可复制且只能移动的 class,其中包含资源应代表的 id
。与 unique_ptr
一样,如果对象移动到另一个对象,id
应该变为 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
给我这个警告:
warning: 'src' used after it was moved [bugprone-use-after-move]
对于dst = std::move(src)
之后的src.get()
(如上标记)。
所以我的问题是:
- 我可以在
std::move(src)
之后调用 src.get()
- 我可以假设
src.get()
returns 0
在 std::move
. 之后
- 如果 1. 和 2. 有效,那么有没有办法更改代码以便 clan-tidy 知道这是有效的。如果不是,有没有办法更改它有效的代码?
下面是class的实现:
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)); }
cppreference.com 有 this text:
Unless otherwise specified, all standard library objects that have
been moved from are placed in a valid but unspecified state. That is,
only the functions without preconditions, such as the assignment
operator, can be safely used on the object after it was moved from:
因此,非正式地,C++ 约定 移出的对象将有效但无用,这就是为什么 clang-tidy 暗示使用它是可疑的.
对于您的实施,您提供的 'more' 比约定的要多 – 所以您的代码没有错,只是非常规。
- Am I allowed to call src.get() after the std::move(src)
如果我们对 src
的类型保持不可知论,那么我们就不知道了。可能不会。在某些情况下,在 move 之后调用成员函数是未定义的。比如调用智能指针的间接运算符。
根据您展示的 decltype(src)
及其成员函数的定义,我们知道:是的,您可以这样做。
- May I make the assumption that src.get() returns 0 after the std::move.
如果我们对 src
的类型一无所知,那么我们对 src.get()
的作用一无所知。更具体地说,我们不知道它的先决条件。
鉴于 decltype(src)
的定义及其您所展示的成员函数:是的,我们可以做出假设。
- If 1. and 2. are valid, then is there a way to change the code so that clan-tidy knows that this is valid. And if not is there a way to change the code that it is valid?
Clang-tidy 假定 "not being in a moved from state" 是所有成员函数(赋值除外)的先决条件,并且在该假设下,警告这种假设的先决条件正在被违反。因此,它试图强制执行一个约定,即使您碰巧知道它不存在于您的 class.
您可以在移动和 src
的重新分配之间删除对 src.get()
的调用。 clang-tidy 不会抱怨的对从变量移动的一个操作是重新赋值,在该赋值之后,对象的状态应该(通常)被很好地定义并且调用其他成员函数被认为是好的(当然,你可以有其他必须满足的先决条件,但 clang-tidy 可能不知道它们)。 虽然从技术上讲,可以定义一个类型,即使在移动后的赋值也没有很好地定义,但这样的类型是非常不寻常和不安全的。
总而言之,您 可以 在移动后(甚至在重新分配之前)为这个特定 class 调用 src.get()
,但是您将不会跟随clang-tidy 试图强制执行的约定。
我正在调整 Herb Sutter 在 cppcon 2016 中谈到的延迟 ptr 的想法,以便能够以更安全的方式管理由 id 表示的外部资源。
因此,我创建了一个不可复制且只能移动的 class,其中包含资源应代表的 id
。与 unique_ptr
一样,如果对象移动到另一个对象,id
应该变为 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
给我这个警告:
warning: 'src' used after it was moved [bugprone-use-after-move]
对于dst = std::move(src)
之后的src.get()
(如上标记)。
所以我的问题是:
- 我可以在
std::move(src)
之后调用 - 我可以假设
src.get()
returns0
在std::move
. 之后
- 如果 1. 和 2. 有效,那么有没有办法更改代码以便 clan-tidy 知道这是有效的。如果不是,有没有办法更改它有效的代码?
src.get()
下面是class的实现:
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)); }
cppreference.com 有 this text:
Unless otherwise specified, all standard library objects that have been moved from are placed in a valid but unspecified state. That is, only the functions without preconditions, such as the assignment operator, can be safely used on the object after it was moved from:
因此,非正式地,C++ 约定 移出的对象将有效但无用,这就是为什么 clang-tidy 暗示使用它是可疑的.
对于您的实施,您提供的 'more' 比约定的要多 – 所以您的代码没有错,只是非常规。
- Am I allowed to call src.get() after the std::move(src)
如果我们对 src
的类型保持不可知论,那么我们就不知道了。可能不会。在某些情况下,在 move 之后调用成员函数是未定义的。比如调用智能指针的间接运算符。
根据您展示的 decltype(src)
及其成员函数的定义,我们知道:是的,您可以这样做。
- May I make the assumption that src.get() returns 0 after the std::move.
如果我们对 src
的类型一无所知,那么我们对 src.get()
的作用一无所知。更具体地说,我们不知道它的先决条件。
鉴于 decltype(src)
的定义及其您所展示的成员函数:是的,我们可以做出假设。
- If 1. and 2. are valid, then is there a way to change the code so that clan-tidy knows that this is valid. And if not is there a way to change the code that it is valid?
Clang-tidy 假定 "not being in a moved from state" 是所有成员函数(赋值除外)的先决条件,并且在该假设下,警告这种假设的先决条件正在被违反。因此,它试图强制执行一个约定,即使您碰巧知道它不存在于您的 class.
您可以在移动和 src
的重新分配之间删除对 src.get()
的调用。 clang-tidy 不会抱怨的对从变量移动的一个操作是重新赋值,在该赋值之后,对象的状态应该(通常)被很好地定义并且调用其他成员函数被认为是好的(当然,你可以有其他必须满足的先决条件,但 clang-tidy 可能不知道它们)。 虽然从技术上讲,可以定义一个类型,即使在移动后的赋值也没有很好地定义,但这样的类型是非常不寻常和不安全的。
总而言之,您 可以 在移动后(甚至在重新分配之前)为这个特定 class 调用 src.get()
,但是您将不会跟随clang-tidy 试图强制执行的约定。