在这个talk中(对声音感到抱歉)Chandler Carruth建议不要在绝大多数情况下通过引用,甚至const引用,因为它限制了后端执行优化的方式。
他声称在大多数情况下副本可以忽略不计 - 我很高兴地相信,大多数数据结构/类等都在堆栈上分配了很小的部分 - 特别是与后端必须假设指针相比时别名以及可以对引用类型执行的所有讨厌的事情。
让我们说我们在堆栈上有大对象 - 比如~4kB和一个对这个对象的实例做某事的函数(假设是独立函数)。
经典我会写:
void DoSomething(ExpensiveType* inOut);
ExpensiveType data;
...
DoSomething(&data);
他建议:
ExpensiveType DoSomething(ExpensiveType in);
ExpensiveType data;
...
data = DoSomething(data);
根据我从谈话中得到的结果,第二种方法倾向于更好地进行优化。虽然我有多大这样的东西是有限制的,或者几乎在所有情况下后端拷贝缺省的东西都会更喜欢这些值?
编辑:为了澄清我对整个系统感兴趣,因为我觉得这对我编写代码的方式有重大改变,我已经使用了refs而不是钻进我的值任何大于整数类型的东西现在都很长。
EDIT2:我也测试了它,结果和代码here。真的没有竞争,因为我们已经教了很长时间,指针是一种快得多的做事方法。现在让我感到兴趣的是为什么在那次演讲中我们提出了我们的价值所在,但是由于这些数字并不支持它,所以我不会这样做。
答案 0 :(得分:2)
我现在已经看过Chandler的部分演讲了。我认为现在的一般性讨论是“我现在应该总是通过价值”#34;不做他的谈话正义。编辑:实际上他之前已经讨论过他的演讲,value semantics vs output params with large data structures以及Eric Niebler的博客,http://ericniebler.com/2013/10/13/out-parameters-vs-move-semantics/。
回到钱德勒。在关键注释中,他特别提到了(在其他地方提到的4x-5x分钟左右),提到了以下几点:
显然,当按值传递(并且可能,如果对象无法移动,返回时),在复制成本和优化优势之间存在折衷。大小和其他使复制成本高昂的东西会使参考策略达到平衡,而对函数中的对象进行大量优化工作会使其转向价值传递。 (他的例子包括指向整数的指针,而不是4k大小的对象。)
根据我观察的部分,我认为钱德勒并不认为钱德勒是一种一刀切的策略。我认为他主要是在传递out参数而不是返回一个新对象的情况下通过引用来传递。他的例子不是关于修改现有对象的函数。
总的来说:
程序应该表达程序员的意图。如果您需要副本,请务必复制!如果要修改现有对象,请务必使用引用或指针。只有当副作用或运行时行为变得无法忍受时;真的只有尝试做一些聪明的事情。
还应该意识到编译器优化有时会令人惊讶。其他平台,编译器,编译选项,类库甚至只是对您自己的代码进行小的更改都可能会阻止编译器拯救。在许多情况下,变更的运行时成本完全出乎意料。
答案 1 :(得分:0)
也许你把这部分话题脱离了背景或某事。对于大型对象,通常取决于函数是否需要对象的副本。例如:
ExpensiveType DoSomething(ExpensiveType in)
{
cout << in.member;
}
当你可以通过const引用传递时,你浪费了大量资源不必要地复制对象。
但如果功能是:
ExpensiveType DoSomething(ExpensiveType in)
{
in.member = 5;
do_something_else(in);
}
我们不想修改调用函数的对象,那么这段代码可能比以下代码更有效:
ExpensiveType DoSomething(ExpensiveType const &inr)
{
ExpensiveType in = inr;
in.member = 5;
do_something_else(in);
}
当使用rvalue调用时会出现差异(例如DoSomething( ExpensiveType(6) );
后者创建一个临时的,复制,然后销毁两者;而前者将创建一个临时的并使用它来移动构造{{1} (我认为这甚至可以进行复制)。
NB。不要将指针用作黑客来实现传递引用。 C ++通过引用原生传递。