例如,如果您有声明std::unique_ptr<A> a;
,那么以下会导致问题吗?
a->foo(std::move(a));
在我的情况下,foo
是一个虚函数,所以我不能把它移出课堂。如果上面的代码结果导致问题,那么有什么方法可以产生相同的效果呢?
答案 0 :(得分:5)
C ++ 11和C ++ 14
这取决于foo
的签名:
foo(std::unique_ptr<A> &&)
,则调用是安全的,因为指针会保留旧值,直到foo
开始执行。 foo
是否更改指针是不相关的,因为在评估命名函数的表达式之后,来自被调用函数执行的任何语句都会被排序。foo(std::unique_ptr<A>)
,则调用具有未定义的行为。虽然有一个规则,在计算运算符的结果之前评估运算符的操作数(因此您必须先评估a
,然后才能知道要调用的foo
,因此称之为),这还不够,因为没有规则说函数参数在函数名表达式之后排序。因此,a
可能已被移除,并且对a.operator ->()
的调用包含尝试取消引用空指针。<强> C ++ 17 强>
从C ++ 17开始,它是安全的。添加了几个新的排序规则,其中一个准确地解决了这个问题:
在函数调用表达式中,在每个参数表达式和每个默认参数之前对命名函数的表达式进行排序。
啊,好吧,我应该为17 C ++之前发布一个安全版本。它非常简单,只需自己添加必要的顺序,例如使用两个陈述:
auto aRaw = a.get();
aRaw->foo(std::move(a));
答案 1 :(得分:1)
我不明白为什么它不合法。
根据这条评论:
a = a->imbue(std::move(a), op, std::move(b));
看起来你想要的是消费操作。也就是说,该函数消耗其输入(所有权被转移到函数中,并且在退出时它们被删除),这正是发生的事情。
其中一个被删除的对象恰好是你调用成员函数的对象,但是虽然看起来就像它可能很棘手,但它确实没问题,因为在调用时,对象仍然保证存在。
您返回一个对象(可能是移动,因为它再次为unique_ptr
),该对象被重新分配给原始名称。所以不是c = a->imbue(a, op, b)
它的 a
= a->imbue(a, op, b)
。
没关系,为什么不会。它与一个刚被删除的对象的名称完全相同,但是那是什么。该对象是有效的,并且没有任何方法可以重新分配与名称不同的东西。当您编写x = x + 1;
时,您也会重新分配与名称不同的内容,没有人会反对。
你可能明确想要避免的是(显然,但可能不那么明显?)在两个位置使用相同的 imbue
对象调用Expression
。