C++ 是",;移动后使用[移动后易出错使用]“;警告这里真的有问题吗?
我正在采用cppcon 2016中提到的不同ptr Herb Sutter的理念,以便能够以更安全的方式管理id所代表的外部资源 因此,我创建了一个不可复制且仅可移动的类,它持有资源应该表示的C++ 是",;移动后使用[移动后易出错使用]“;警告这里真的有问题吗?,c++,c++17,move,clang-tidy,C++,C++17,Move,Clang Tidy,我正在采用cppcon 2016中提到的不同ptr Herb Sutter的理念,以便能够以更安全的方式管理id所代表的外部资源 因此,我创建了一个不可复制且仅可移动的类,它持有资源应该表示的id。与unique\u ptr类似,如果将对象移动到另一个对象,则id应变为0 据我所知,即使在对象被移动之后,如果被调用函数没有任何先决条件,您仍然应该被允许使用该对象,因此据我所知,这应该是有效的: int main() { resource src = make_resource(10);
id
。与unique\u 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;
}
cppreference.com有:
除非另有规定,否则所有具有
已从中移动的对象处于有效但未指定的状态。就是,
只有不带前置条件的函数,例如赋值
操作员,可在对象从以下位置移动后安全使用:
P> >,非正式地,C++约定是从对象移动的是无效的,这就是为什么CLAN-TIY建议使用它是可疑的。
对于您的实现,您提供了“更多”而非约定——因此您的代码没有错,只是非常规
src
的类型仍然不可知,那么我们就不知道了。可能不会。在某些情况下,在移动后调用成员函数是未定义的。例如,调用智能指针的间接运算符
根据您展示的decltype(src)
及其成员函数的定义,我们知道:是的,您可以
src
的类型仍然不可知,那么我们对src.get()
的功能一无所知。更具体地说,我们不知道它的先决条件
给出了decltype(src)
的定义以及您展示的成员函数:是的,我们可以做出假设
src
之间删除对src.get()
的调用。clang tidy没有抱怨的moved from变量上的一个操作是重新赋值,在赋值之后,对象的状态应该(按照惯例)得到很好的定义,并且调用其他成员函数被认为是正常的(当然,你可以有其他必须满足的先决条件,但clang tidy可能不知道这些条件)。虽然从技术上讲,可以定义一种类型,即使是移动后的分配也没有很好的定义,但这种类型将非常非传统和不安全
总之,您可以在移动之后(甚至在重新分配之前)调用
src.get()
对于这个特定的类,但是你不会遵循clang tidy试图强制执行的约定。我认为这很好,但是clang tidy不知道你的智能指针及其语义,因此假设情况更糟。你没有编写代码吗?如果是,你应该是回答这些问题的最佳人选。没有太大区别在从资源
移动的一个对象和一个恰好具有id==0
的有效对象之间,没有?@juanchopanza如果我使用调试器,那么我看到一切都按预期工作。但这并不意味着这是未定义的行为。@user463035818据我所知,这个类管理的任何有效对象都不能具有id=0,由外部API保证。这是有道理的。我非常关注我的实现是否正确,以至于我忘记了一个事实,即在移动后,即使对象是正确的,您自然也不会访问它。因此在正常使用情况下,不会出现叮当声警告,因为您永远不应该调用.get()
直接在移动之后,而这正是clang tidy警告我的。但是我可以稍后在其他地方调用.get()
来检查我的类是否仍在引用外部资源,或者“所有权”是否被移走。@t.niese就是这样,有时你会调用.get()
,因为这完全取决于要从中移动的对象的类型。这就是我为什么对代码的作者发表评论的原因。clang tidy必须做出假设才能正常工作,有时这些假设是错误的。公平地说,这句话没有提到非标准库对象-我意识到你是故意的这是一个非正式的惯例,因为你意识到你在扩大报价的范围,这不是不合理的。但我不确定我们是否能走得那么远,我想这就是我想说的。嗯“有效但无用”对于其接口没有可调用的前置条件的对象来说有点强。实现只提供了约定的内容。只是pre()
没有前置条件。我的意思是get()
,而不是pre()“LeNNESS RESIMSNORE”非正式约定“可能不是正确的术语,但我的意思是,IMO,设计一些东西是不明智的,即使C++不能执行,也会让那些理解C++标准库的人感到惊讶。举个愚蠢的例子,除了惯例,没有什么能阻止你做容器。班级里的冰毒在哪里
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)); }