当 C++(隐式或显式)值构造函数需要以任何一种方式在其对象中存储参数的副本时,是否应该通过值或常量引用接受其参数?

这是我能想到的最短的例子:

struct foo {
    bar _b;
    foo(bar [const&] b) // pass by value or reference-to-const?
        : _b(b) { }
};

这里的想法是,当创建 foo 对象时,我希望以创建 foo 对象的各种方式最小化对 bar 的复制构造函数的调用。

请注意,我确实对复制省略和(命名)返回值优化有一点了解,并且我已阅读 “想要速度吗?按值传递”, ,但是我认为这篇文章并没有直接解决这个用例。

编辑: 我应该更具体一点。

假设我无法知道 sizeof(bar), ,或者是否 bar 是一个基本的内置类型(bar 可以是模板参数,并且 foo 可能是类模板而不是类)。另外,不要假设 foo的构造函数可以内联(或 bar的,就此而言)。请假设我至少可能使用实现 RVO 的编译器。

我希望有可能(考虑到编译器优化)这样的调用不会调用 bar的复制构造函数(即使在执行时 _b(b)foo的初始化列表):

foo f = function_that_creates_and_returns_a_bar_object_using_rvo();

是否有可能(考虑到 C++98 标准)可以做到这一点,如果是的话,如果 foo 通过引用 const 而不是通过值接受其参数?

有帮助吗?

解决方案

在C ++ 98和C ++ 03,你应该通过const& bar,然后复制。 C ++ 0x中,你应该通过bar,然后执行一个移动(提供bar具有移动构造函数)。

#include <utility>

struct foo
{
    bar _b;

    foo(bar b) : _b(std::move(b)) {}
};

如果您构建富与左值参数,拷贝构造函数会被调用,以创建一个副本b,并且该副本将被转移到_b。如果您构建富与右值参数,bar的移动构造函数将被调用来进入b,然后它会被再次转移到_b

其他提示

在所有条件相同的情况下,我通过 const& 来表示足够复杂的类,并通过 value 来表示 POD 和简单对象。

列出通过 const 引用传递而不是传统的按值传递的优点/缺点

优点:

  • 避免复制(对于具有昂贵副本的对象来说是一大优势)
  • 只读访问

缺点:

  • 如果有人真的愿意,可以 const 将 const 从引用中删除

更重要的是积极的一面是你 明确地 控制复制发生的时间(在您的情况下,在初始化列表中传递初始化 _b 时)。考虑负面因素...我同意这是一个风险。我认为几乎所有优秀的程序员都会对使用 const_cast 感到肮脏。此外,您可以尽职尽责地搜索 const_cast 并向将 const 从参数中剔除的人扔西红柿。但是嘿,你永远不知道谁有时间像鹰一样观察代码:)?

我的主观意见是,在足够复杂的类和性能很重要的环境中,避免复制构造函数的好处超过了风险。然而,对于真正愚蠢的 POD 类,我倾向于复制数据并按值传递。

看此问题

使用const T & arg如果sizeof(T)>sizeof(void*)并使用T arg如果sizeof(T) <= sizeof(void*)。所有基本类型应该是例外

文体上,我会说,通过引用传递是更好的方法。

如果性能真的很重要,那么就不要猜。测量它。

我没有检查的标准说什么,而是试图通过这个经验,我可以告诉你,GCC不优化复制走,不管构造是否接受通过值或const引用的论点。

如果,然而,构造函数采用一个const参考,它管理当从创建 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 对象的成员字段)。取而代之的是,它需要的目标是在堆栈的顶部。

我假定bar具有形式bar(const bar &b)的正常拷贝构造

这里以一个常量引用。然后bar的构造将采取参考,并执行复制。总副本:1

如果你走的参考源,编译器会使得B的副本,通过副本foo的构造函数,然后将它传递给bar和复制。

老实说,对于这种情况,最好的办法是让你的构造inline,提供bar的构造方法不抛出。然后,只需通过引用传递。

此外,如你怀疑,你的文章在这里并不适用。

持shared_pointer在你的类吧,并将它传递本身。这样,你永远不会调用拷贝构造函数:)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top