在构造函数初始化列表中移动shared_ptr
move shared_ptr on constructor initialization list
最近我看到了一些类似这样的代码示例,其中在构造函数初始化列表中使用了std::move(而不是move构造函数)。
class A {
public:
A(std::shared_ptr<Res> res) : myRes(std::move(res)) {
// ...
}
private:
std::shared_ptr<Res> myRes;
}
我得到的信息是,这种建设是出于优化的原因。就我个人而言,我尽量少用std::move。我威胁它们为cast(正如Scott Meyers所说),并且仅在调用程序代码中(唯一的例外是move构造函数)。对我来说,这看起来像是某种模糊处理或微优化,但也许我错了。如果没有std::move,编译器不会生成更快的代码,这是真的吗?
我认为缺少std::move()
,其中可以移动一个非平凡的对象,但编译器无法检测到这是代码中的错误。也就是说,构造函数中的std::move()
是强制性的:很明显,用它调用构造函数的临时对象即将超出范围,即可以安全地将其移出。另一方面,从参数构造成员变量并不是可以省略副本的情况之一。也就是说,编译器必须创建一个副本,这对于std::shared_ptr<T>
来说当然不是很昂贵,但也不是免费的。特别是,更新的引用计数需要同步。这种差异是否可以衡量是另一个问题。运行一个简单的基准测试(见下文)似乎意味着确实存在性能改进。通常我得到的结果是这样的:
// clang:
copy: 440
move: 206
copy: 414
move: 209
// gcc:
copy: 361
move: 167
copy: 335
move: 170
请注意,在此上下文中,您是成员构造函数的调用方!std::move(res)
只是编写演员阵容的一种奇特方式(它是static_cast<std::shared_ptr<RES>&&>(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);
}
- CLANG 编译器 说:变量"PTR"可能未初始化
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 将 ptr 传递给 ptr 到 A 作为参数传递给 A 的函数是不好的做法吗?
- 为共享 ptr 向量实现复制 c'tor?
- 字符和整数中 **(ptr+1) 的值差异
- C++:在不中断共享的情况下通过引用传递共享 PTR?
- 如何将派生类从基 ptr 分配给 nlohmann::json
- 引用 std::shared:ptr 以避免引用计数
- 为什么我不能在不进行任何转换的情况下将浮点数放入任何类型的 ptr 中?
- 在调用函数时,ptr** 和 ptr*& 之间是否有区别,或者首选C++?
- 另一种类型的智能ptr,比如具有弱refs的unique_ptr
- 尝试打印出 *ptr++ 的值,以了解它是如何工作的
- 如何控制共享 ptr 引用计数?
- dopen():不以 root 身份运行时"failed to map segment from shared object"
- C++中的指针否定 (!ptr == NULL)
- 从const ptr*转换为ptr*时出现问题
- 无法使用 libtool 将 -shared 参数传递给 g++
- boost::shared_ptr和std::shared-ptr的同居
- 我可以用std::shared_ptr而不是boost::shared-ptr构建boost库吗
- shared-ptr-C++shared_ptr与unique_ptr用于资源管理