如何以更好的性能传递和共享 shared_ptr 所有权?

How to pass and share shared_ptr ownership with a better performance?

例如

class A
{
public:
    // Option 1
    void setI_1(shared_ptr<int> i) { m_i = i; }

    // Option 2
    void setI_2(shared_ptr<int> i) { m_i = move(i); }

    // Option 3
    void setI_3(shared_ptr<int> const& i) { m_i = i; }

private:
    shared_ptr<int> m_i;        
};

通常哪个选项更好?

我在 Visual Studio 2017 年做基准测试。选项 2 在我测试的所有情况下都给了我最好的性能。在某些情况下,选项 3 的性能与选项 2 相似,但在某些情况下更差。谢谢!

在你的情况下,选项 2 是最好的情况。您可能希望将指针移动到 class,这样当您不再需要它时,就不需要一次又一次地增加和减少 reference-counter:

{
    shared_ptr<int> ptr = std::make_shared<int>(42);

    A a;
    a.setI_2(std::move(ptr)); // I no longer need ptr, so I can move it
};

指针移动到参数列表,ptr 是 null/the 参数用 r-value-reference 引用 ptr 初始化,因此 ptr 为空。然后从参数列表中移到成员中​​,参数为空。你看,没有(需要)副本,这意味着 reference-counter.

的变化

我更喜欢 T const& 仅适用于仅从引用读取且不用于初始化另一个变量的函数。如果在示例中使用了带有引用的第三个选项,则需要复制指针,因为 setI_3 的主体在赋值语句中有一个副本(没有移动)。

第一个选项显然是最差的,因为指针被复制了多次。

当您将 shared_ptrunique_ptr 交换时,编译器会强制您使用尽可能快的方式 - 移动。

我要添加第四个选项:

// Option 4
void setI_4(shared_ptr<int>&& i) { m_i = move(i); }

请注意,即使 i 是 r-value-reference,它在代码中使用时也不是 r-value-reference,因此在没有移动时会被复制;

第四个选项仅与第二个选项不同,传递的参数始终需要是一个 r-value-reference,而在第二个选项中,i 本身就是一个 pointer-variable在第四个 i 中是一个引用(函数主体可以用它做任何事情,但它存在于函数之外(std::move 允许这样做))。 parameter-variable 是从调用者的 parameter-tuple 中的表达式构造的,如果 shared_ptr 的构造函数自动复制指针(这意味着 reference-counter 增加)不是 r-value-reference.

setI_2的调用方式如下:

{
    shared_ptr<int> ptr = std::make_shared<int>(42);
    /// ptr owns 42
    A a;
    {   // a.setI_2(std::move(ptr));
        // initializing the parameter
        shared_ptr<int> i{std::move(ptr)};
        // now i owns 42 and ptr no longer owns it and points to null
        {   // the body of the function
            a.m_i = std::move(i);
            // now a.m_i owns 42 and i no longer owns it and points to null
        };
    };
};