【问题标题】:Can you use make_unique directly in a function call?你可以直接在函数调用中使用 make_unique 吗?
【发布时间】:2019-09-25 10:30:42
【问题描述】:

我正在尝试更新一些交付新对象的代码。目标是使用智能指针对其进行管理。归结起来,它看起来很像这样:

class X
{
    shared_ptr<Y> yptr;

    X() : yptr(Y::create(new Z())){}
};

class Y
{
    Z* zptr;
    static shared_ptr<Y> create(Z* zp)
    {
        if(!zp) return nullptr;
        else return shared_ptr<Y>(new Y(zp));
    }

    Y(Z* zp) : zptr(zp){}
}

到目前为止,这似乎有效:

class X
{
    shared_ptr<Y> yptr;

    X() : yptr(Y::create(  std::move(  std::make_unique<Z>(Z())  )  )){}
};

class Y
{
    unique_ptr<Z> zptr;
    static shared_ptr<Y> create(unique_ptr<Z> zp)
    {
        if(!zp) return nullptr;
        else return shared_ptr<Y>(new Y(std::move(zp)));
    }

    Y(unique_ptr<Z> zp) : zptr(std::move(zp)){}
}

我的问题是,第一个 std::move() (围绕 make_unique)是否必要? Visual Studio 似乎并不介意任何一种方式。在开始对性能更为关键的其他地方进行类似更改之前,我宁愿有一个准确的了解。

【问题讨论】:

  • 如果你真的想要std::shared_ptr,那么使用std::make_shared()有什么问题?
  • std::move 所做的只是将对象转换为纯右值。如果对象已经是纯右值(例如,它没有名称......它是临时的),那么std::move 是多余的。
  • @πάντα-ῥεῖ 所涉及的代码是一个工厂方法,它在创建时分配其成员。它返回的对象旨在成为由 unique_ptr 管理的项目的唯一所有者。在这种情况下,shared_ptr 可以工作,但它背叛了意图。
  • @françois-andrieux 是的,我会调整上面的。 :)

标签: c++ c++14 smart-pointers


【解决方案1】:

是的! make_unique 的移动是不必要的。

std::move 的存在是将左值引用 &amp;(指现有对象)转换为右值引用 &amp;&amp;(表示临时值,或可以从中移动的东西)。

如果您将unique_ptr 分配给命名变量,那么如果您想转移其所有权(通常;见下文),您确​​实需要从中分配std::move。您不能直接将 r 值引用绑定到变量。

表达式或函数调用返回的值,例如make_shared返回的unique_ptr,是一个临时值,因此它自然可以绑定到一个右值表达式并隐式移出。

请注意,在某些特殊情况下,您需要使用std::move:

  • 如果你有一个类型为右值引用的命名变量,你确实需要std::move它以避免通过复制传递它:

    MyClass(std::string&amp;&amp; s) : m_mystring(s) {} 复制-构造m_mystring

    MyClass(std::string&amp;&amp; s) : m_mystring(std::move(s)) {}move-constructs m_mystring

    通常,具有名称的变量不会隐式移出。

  • 如果您从函数中按值返回局部变量,则该对象可能会自动移出,即所谓的Named Return Value Optimization。这为按值返回提供了很好的速度提升。在实践中您无需考虑太多,只需注意return std::move(whatever); 几乎总是错误的。

【讨论】:

  • 很好的解释。我有一个跟进,因为你提出了类似的情况。如果传递 make_unique 的函数必须在实际使用之前将其传递给构造函数,那么 std::move() 现在是必需的,还是仅在构造函数本身(最终目的地)内部需要?
  • @M.Parker 来自 Scott Meyers 的 Essential Modern C++,“参数始终是左值,即使它的类型是右值引用。也就是说,给定 @987654337 @,参数w是一个左值,尽管它的类型是rvalue-reference-to-Widget。"
  • 这似乎是一个小问题,但问题是“是第一个 std::move() 必需”,(不是“是第一个 std::move () unnecessary)。所以你的回答实际上是“不!”,而不是“是!”。我出轨了几分钟试图理解你的回答,因为我看到了你的“是” !”但错过了您的“联合国”,并认为您所说的与您实际所说的相反。
【解决方案2】:

简短回答:是

函数调用std::make_unique&lt;Z&gt;()返回一个右值,所以如果你直接将它传递给另一个函数,则不需要将它包装在std::move()中。

【讨论】:

    【解决方案3】:

    我的问题是,第一个 std::move()(围绕 make_unique)是否必要?

    如果你指的是这一行:

    X() : yptr(Y::create(  std::move(  std::make_unique<Z>(Z())  )  )){}
    

    那么不,这是完全没有必要的,因为std::make_unique&lt;Z&gt; 返回临时的已经是prvalue。你可以这样做:

    auto ptr = std::move( std::move( std::move( std::make_unique<Z>() ) ) );
    

    而不是

    auto ptr = std::make_unique<Z>();
    

    但是moves 的所有内容都是不必要的,它被编译的事实清楚地表明了这一点。

    【讨论】:

      猜你喜欢
      • 2019-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-30
      • 1970-01-01
      相关资源
      最近更新 更多