当一个函数接受一个shared_ptr
(来自boost或C ++ 11 STL)时,是否要传递它:
通过const参考:
void foo(const shared_ptr<T>& p)
或值:
void foo(shared_ptr<T> p)
?
我更喜欢第一种方法,因为我怀疑它会更快。但这真的值得吗?还是有其他问题?
请问您提供选择的理由,或者如果是这种情况,请说明原因。
shared_ptr
类成员。你通过const-refs做到吗?
当一个函数接受一个shared_ptr
(来自boost或C ++ 11 STL)时,是否要传递它:
通过const参考: void foo(const shared_ptr<T>& p)
或值:void foo(shared_ptr<T> p)
?
我更喜欢第一种方法,因为我怀疑它会更快。但这真的值得吗?还是有其他问题?
请问您提供选择的理由,或者如果是这种情况,请说明原因。
shared_ptr
类成员。你通过const-refs做到吗?
Answers:
斯科特,安德烈(Andrei)和赫伯(Herb)在C ++及以后的2011年的《任何问题》会议上讨论并回答了这个问题。从4:34 开始观看性能和正确性。shared_ptr
不久,没有理由按值传递,除非目标是共享对象的所有权(例如,在不同的数据结构之间或在不同的线程之间)。
除非您可以按照上面链接的视频中Scott Meyers的说明进行移动优化,但这与您可以使用的C ++的实际版本有关。
在GoingNative 2012会议的“ 交互式面板:任何问题!”中对该讨论进行了重大更新。值得一看,尤其是从22:50开始。
Value*
简短易读,但是很糟糕,所以现在我的代码已经写满了const shared_ptr<Value>&
,可读性也大大降低了,整洁了。void Function(Value* v1, Value* v2, Value* v3)
现在过去是什么void Function(const shared_ptr<Value>& v1, const shared_ptr<Value>& v2, const shared_ptr<Value>& v3)
,人们对此表示满意吗?
class Value {...}; using ValuePtr = std::shared_ptr<Value>;
然后您的函数变得更简单:void Function(const ValuePtr& v1, const ValuePtr& v2, const ValuePtr& v3)
并且您将获得最佳性能。这就是使用C ++的原因,不是吗?:)
这是赫伯·萨特的
准则:除非您要使用或操纵智能指针本身(例如共享或转让所有权),否则请勿将智能指针作为函数参数传递。
指导原则:表示函数将使用按值shared_ptr参数存储和共享堆对象的所有权。
准则:仅使用非常量shared_ptr&参数来修改shared_ptr。仅在不确定是否要复制并共享所有权时,才使用const shared_ptr&作为参数。否则,请改用widget *(或者,如果不能为null,则使用widget&)。
我跑到下面的代码,一旦与foo
服用shared_ptr
的const&
,并再次foo
取shared_ptr
的值。
void foo(const std::shared_ptr<int>& p)
{
static int x = 0;
*p = ++x;
}
int main()
{
auto p = std::make_shared<int>();
auto start = clock();
for (int i = 0; i < 10000000; ++i)
{
foo(p);
}
std::cout << "Took " << clock() - start << " ms" << std::endl;
}
在我的Intel Core 2 Quad(2.4GHz)处理器上使用VS2015,x86版本构建
const shared_ptr& - 10ms
shared_ptr - 281ms
按值复制版本要慢一个数量级。
如果要从当前线程同步调用函数,请选择该const&
版本。
foo()
函数甚至根本不应该接受共享指针,因为它没有使用该对象:它应该接受a int&
和do p = ++x;
,foo(*p);
从调用main()
。函数需要在需要执行某操作时接受一个智能指针对象,并且在大多数情况下,您需要将其移动std::move()
到其他地方,因此按值参数没有成本。
从C ++ 11开始,您应该比您想像的更多地重视const&上的价值。
如果您使用的是std :: shared_ptr(而不是基础类型T),那么您这样做是因为您想对此做一些事情。
如果您想将其复制到某处,则更有意义的是将其复制并std ::内部移动,而不是通过const&进行复制然后再复制。这是因为在调用函数时,允许调用者选项依次std :: move shared_ptr,从而为自己节省了一组递增和递减操作。或不。也就是说,函数的调用者可以在调用函数之后并根据是否移动来决定是否需要std :: shared_ptr。如果您通过const&,这是无法实现的,因此最好按值取值。
当然,如果调用者都需要他的shared_ptr更长的时间(因此无法std :: move),并且您不想在函数中创建纯副本(例如,您想要一个弱指针,或者您有时只想复制它(取决于某些条件),那么const&可能仍然是首选。
例如,您应该
void enqueue(std::shared<T> t) m_internal_queue.enqueue(std::move(t));
过度
void enqueue(std::shared<T> const& t) m_internal_queue.enqueue(t);
因为在这种情况下,您总是在内部创建副本
最近有一篇博客文章:https : //medium.com/@vgasparyan1995/pass-by-value-vs-pass-by-reference-to-const-c-f8944171e3ce
因此,答案是:不要(几乎)永不过去const shared_ptr<T>&
。
只需传递基础类即可。
基本上,唯一合理的参数类型是:
shared_ptr<T>
-修改并取得所有权shared_ptr<const T>
-不要修改,要拥有所有权T&
-修改,无所有权const T&
-不要修改,没有所有权T
-请勿修改,没有所有权,可以廉价复制正如@accel在https://stackoverflow.com/a/26197326/1930508中指出的那样,来自Herb Sutter的建议是:
仅在不确定是否要复制并共享所有权时,才使用const shared_ptr&作为参数
但是,您不确定有多少种情况?所以这是一种罕见的情况
众所周知的问题是,按值传递shared_ptr会产生成本,应尽可能避免。
大多数情况下,通过引用传递shared_ptr,甚至通过const引用传递更好。
cpp核心准则有一个用于传递shared_ptr的特定规则
void share(shared_ptr<widget>); // share -- "will" retain refcount
确实有必要在值上传递shared_ptr的一个示例是,当调用方将共享对象传递给异步被调用方时-即,在被调用方完成其工作之前,调用方超出范围。被调用者必须通过按值获取share_ptr来“延长”共享对象的寿命。在这种情况下,不会传递对shared_ptr的引用。
将共享库传递到工作线程也是如此。
shared_ptr不够大,其构造函数\析构函数也没有做足够的工作以使副本有足够的开销来关心按引用传递与按副本传递的性能。
shared_ptr<int>
by值会占用100多个x86指令(包括昂贵的lock
ed指令,以原子方式增加/减少引用计数)。传递常量ref与传递指向任何对象的指针相同(在此示例中,在Godbolt编译器资源管理器上,尾部调用优化将其转换为简单的jmp而不是调用:godbolt.org/g/TazMBU)。
shared_ptr
,如果需要我可以更改它。”,而值版本则说“我要复制您的shared_ptr
,因此尽管我可以更改它,但您永远不会知道。 )const-reference参数才是真正的解决方案,它表示“我要为某些别名shared_ptr
,并且保证不会更改。”(这与按值语义非常相似!)