【问题标题】:move shared_ptr on constructor initialization list在构造函数初始化列表上移动 shared_ptr
【发布时间】:2014-11-01 02:16:24
【问题描述】:

最近我看到很少有这样的代码示例,其中 std::move 用于构造函数初始化列表(而不是移动构造函数)。

class A {
public:
    A(std::shared_ptr<Res> res) : myRes(std::move(res)) {
    // ...
    }

private:
    std::shared_ptr<Res> myRes;
}

我得到信息,这个结构是出于优化的原因。就我个人而言,我尽可能少地使用 std::move 。我威胁他们作为演员(正如斯科特迈耶斯所说),并且只在调用者代码中(唯一的例外是移动构造函数)。对我来说,这看起来像是某种混淆或微优化,但也许我错了。是真的吗,编译器不会产生更快的代码,没有 std::move?

【问题讨论】:

  • 我认为目的是避免引用计数器的原子增量。这也是一个准确表达您的意图的问题,并且是一种相当常见的模式(按值传递,然后移动本地参数)。
  • @user2079303 我不确定它是否是该特定问题的完整副本。主要关注的是在移动构造函数中移动数据成员;这与这个问题中的问题不同。不过,性能部分部分在那里得到了回答。
  • 公平点,我撤回了投票,但我会留下链接,因为我认为它与性能方面有关。

标签: c++ c++11 optimization move move-semantics


【解决方案1】:

我认为缺少std::move() 可以移动一个重要的对象,但编译器无法检测到这是代码中的错误。也就是说,构造函数中的std::move() 是强制性的:显然,调用构造函数的临时对象即将超出范围,即可以安全地从中移出。另一方面,从参数构造成员变量不是可以省略副本的情况之一。也就是说,编译器必须创建一个副本,这对于std::shared_ptr&lt;T&gt; 来说肯定不是很贵,但它也不是免费的。特别是,更新的引用计数需要同步。是否可以测量差异是一个不同的问题。运行一个简单的基准测试(见下文)似乎意味着确实存在性能改进。通常我得到的结果是这样的:

// clang:
copy: 440
move: 206
copy: 414
move: 209
// gcc:
copy: 361
move: 167
copy: 335
move: 170

注意,在这种情况下,您成员构造函数的调用者! std::move(res) 只是编写演员表的一种奇特方式是正确的(它是 static_cast&lt;std::shared_ptr&lt;RES&gt;&amp;&amp;&gt;(res) 的替代品)。但是,在对象即将超出范围但被复制的地方使用它是至关重要的。从语义上讲,std::move() 的使用在许多情况下是无关紧要的(它仅在处理可移动但不可复制的类型时才在语义上相关)。避免不必要的复制是一项重要的性能改进,std::move() 有助于在编译器无法推断它是好的或不允许这样做的情况下这样做:具体情况是编译器甚至可能检测到的它自己认为移动是安全的,但不允许用移动替换副本。如果编译器在这些情况下会警告缺少std::move(),那就太好了!

#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <memory>
#include <ostream>
#include <vector>

class timer
{
    typedef std::chrono::high_resolution_clock clock;
    clock::time_point d_start;
public:
    timer(): d_start(clock::now()) {}
    std::ostream& print(std::ostream& out) const {
        using namespace std::chrono;
        return out << duration_cast<microseconds>(clock::now() - this->d_start).count();
    }
};

std::ostream& operator<< (std::ostream& out, timer const& t)
{
    return t.print(out);
}

struct ResCopy
{
    std::shared_ptr<unsigned int> d_sp;
    ResCopy(std::shared_ptr<unsigned int> sp): d_sp(sp) {}
    unsigned int value() const { return *this->d_sp; }
};

struct ResMove
{
    std::shared_ptr<unsigned int> d_sp;
    ResMove(std::shared_ptr<unsigned int> sp): d_sp(std::move(sp)) {}
    unsigned int value() const { return *this->d_sp; }
};

template <typename Res>
void measure(char const* name, std::vector<std::shared_ptr<unsigned int>> const& v)
{
    timer t;
    unsigned long value(0);
    for (int c(0); c != 100; ++c) {
        for (std::size_t i(0), end(v.size()); i != end; ++i) { 
            value += Res(v[i]).value();
        }
    }
    std::cout << name << ": " << t << '\n';
}

int main()
{
    std::vector<std::shared_ptr<unsigned int>> v;
    std::generate_n(std::back_inserter(v), 100,
                    []{ return std::shared_ptr<unsigned int>(new unsigned int(std::rand())); });

    measure<ResCopy>("copy", v);
    measure<ResMove>("move", v);
    measure<ResCopy>("copy", v);
    measure<ResMove>("move", v);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-19
    • 1970-01-01
    相关资源
    最近更新 更多