C ++(隐式或显式)值构造函数是否应该通过value或reference-to-const接受其参数,当它需要以任何方式在其对象中存储参数的副本时?
这是我能想到的最短的例子:
struct foo {
bar _b;
foo(bar [const&] b) // pass by value or reference-to-const?
: _b(b) { }
};
这里的想法是,我希望在创建foo对象时,以创建foo对象的各种方式最小化对bar的复制构造函数的调用。
请注意我对copy elision和(Named)返回值优化有一点了解,并且我已阅读"Want Speed? Pass by Value",但我不认为该文章直接针对此用例。
编辑:我应该更具体。
假设我无法知道sizeof(bar)
,或者bar
是否是基本的内置类型(bar
可能是模板参数,{{1可能是类模板而不是类)。另外,不要假设foo
的构造函数可以内联(或foo
),就此而言。假设我至少可能使用实现RVO的编译器。
我希望有一种可能性(给定编译器优化)这样的调用不会调用bar
的复制构造函数(即使在bar
中执行_b(b)
{1}}的初始化列表):
foo
是否有可能(根据C ++ 98标准)可以做到这一点,如果是这样,如果foo f = function_that_creates_and_returns_a_bar_object_using_rvo();
通过引用到const接受其参数,它或多或少有可能工作按值计算?
答案 0 :(得分:5)
一切都是平等的,我通过const& amp;对于足够复杂的类,以及POD和简单对象的值。
规定优点/缺点是通过const引用而不是传统的值传递
肯定:
否定:
更重要的是,积极的是你在复制发生时明确控制(在初始化列表中初始化_b的情况下)。思考负面因素......我同意这是一种风险。我认为几乎所有优秀的程序员都会因为使用const_cast而感到肮脏。此外,你可以尽职尽责地搜索const_cast并将西红柿扔给那些从const中抛出const的人。但嘿,你永远不知道,谁有时间像鹰一样看代码?)?
我的主观意见是,在足够复杂的类和性能重要的环境中,避免复制构造函数的好处超过了风险。然而,对于非常愚蠢和POD类,我倾向于复制数据并按值传递。
答案 1 :(得分:4)
看看question。
const T & arg
使用sizeof(T)>sizeof(void*)
,T arg
使用sizeof(T) <= sizeof(void*)
。所有基本类型都应该是此规则的例外
答案 2 :(得分:2)
在风格上,我会说通过引用传递是更好的方法。
如果表现真的很重要,那就不要猜。测量它。
答案 3 :(得分:2)
我没有检查标准所说的内容,但通过经验尝试,我可以告诉你GCC不会优化副本,无论构造函数是通过值还是通过const引用接受参数。
但是,如果构造函数采用const引用,则当您从现有的 bar 对象创建 foo 时,它会设法避免不必要的副本。
总结:
Bar b = makeBar(); // No copy, because of RVO
FooByValue f1 = makeBar(); // Copy constructor of Bar called once
FooByRef f2 = makeBar(); // Copy constructor of Bar called once
FooByValue f3(b); // Copy constructor of Bar called twice
FooByRef f4(b); // Copy constructor of Bar called only once
不是说我是编译专家,但我认为通常不能将RVO返回到任意位置(例如 foo 对象的成员字段)。相反,它需要目标位于堆栈的顶部。
答案 4 :(得分:2)
在C ++ 98和C ++ 03中,您应该传递const& bar
然后复制。在C ++ 0x中,您应该传递bar
然后进行移动(前提是bar
有一个移动构造函数)。
#include <utility>
struct foo
{
bar _b;
foo(bar b) : _b(std::move(b)) {}
};
如果使用lvalue参数构造foo,则将调用复制构造函数以创建副本b
,并将该副本移动到_b
。如果使用rvalue参数构造foo,则会调用bar
的移动构造函数移动到b
,然后它将再次移动到_b
。
答案 5 :(得分:1)
我假设bar
有一个bar(const bar &b)
形式的普通复制构造函数
在此处获取const引用。然后bar
的构造函数将获取该引用,并执行复制。总份数:1。
如果你关闭了引用,编译器会复制b,将副本传递给foo
的构造函数,然后将其传递给bar
并复制它。
答案 6 :(得分:0)
老实说,对于这种情况,最好的选择是使你的构造函数inline
,只要bar
的构造函数不抛出。然后通过引用传递。
此外,正如您所怀疑的那样,您的文章不适用于此。
答案 7 :(得分:-1)
将一个shared_pointer保存到您班级的栏中并按原样传递。这样你永远不会调用复制构造函数:)。