【问题标题】:Is it legal to call a method on a class with a unique_ptr as an argument?在具有 unique_ptr 作为参数的类上调用方法是否合法?
【发布时间】:2017-11-19 08:06:14
【问题描述】:

例如,如果你有一个声明std::unique_ptr<A> a;,那么下面会导致问题吗?

a->foo(std::move(a));

在我的例子中,foo 是一个虚函数,所以我不能将它移出类。如果上面的代码结果导致问题,那么有什么替代方法可以产生相同的效果?

【问题讨论】:

  • 函数是否应该在调用期间拥有被指向的对象?这确实是最重要的问题。
  • 好问题,但不确定您到底在问什么。在我的案例中使用的真正方法是 virtual std::unique_ptr Expression::imbue( std::unique_ptr a, Operator o, size_t priority, std::unique_ptr b);它被称为:a = a->imbue(std::move(a), op, std::move(b));
  • 您确定要在一个类上调用std::move 将调用哪个方法?在imbue 将被调用时,您的a 将被销毁,因此该方法很可能具有无效的上下文。
  • @user3707125 - “在 imbue 将被调用时,你的 a 将被销毁”不,不会。该对象是活动的,并且在调用期间由唯一指针拥有。该对象在退出时立即被销毁,而不是更早。
  • @StoryTeller 我的意思是被std::move 操作破坏,不是内存释放或类似的东西,而是它的数据将不再有效。

标签: c++


【解决方案1】:

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 开始,它是安全的。添加了几条新的排序规则,其中一条正好解决了这种情况:

在函数调用表达式中,命名函数的表达式在每个参数表达式和每个默认参数之前排序。

Source

啊,好吧,我应该为 17 之前的 C++ 发布一个安全版本。这很简单,只需自己添加必要的排序即可,例如通过使用两个语句:

auto aRaw = a.get();
aRaw->foo(std::move(a));

【讨论】:

    【解决方案2】:

    我不明白为什么它不合法。

    根据这条评论:

    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 对象。

    【讨论】:

      猜你喜欢
      • 2011-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-18
      • 1970-01-01
      • 2015-07-20
      • 1970-01-01
      相关资源
      最近更新 更多