【问题标题】:return a std::array返回一个 std::array
【发布时间】:2012-03-20 18:26:24
【问题描述】:

我正在实现一些类来表示映射(在数学意义上),即 f : R x R^n -> R^n。我想实现一个抽象基类: 1) 将 std::array 作为引用并对其进行修改 要么 2) 返回一个 std::array (按值还是按引用,不确定?)

选项 2 的语法对我来说更可取,因为我认为代码看起来更像我试图表示的数学,但我想确保我不会不必要地复制东西并引发一堆不受欢迎的东西高架。例如,如果我有:

// Function f declaration
std::array<double, 4> f(double t, const std::array<double, 4> & x);

// Some code snippet that uses f
std::array<double, 4> x = {0.0, 1.0, 2.0, 3.0};
double t = 0.0;
std::array<double, 4> dxdt = f(t, x);

如何确定是否在最后一行执行了复制,或者如何确保它不会发生?

在 f() 的定义中,我需要做什么(如果有的话)以确保在不调用复制构造函数的情况下返回 this?我想保持使用简单,这样客户端就不需要使用指针或智能指针,但也许这是必要的?

我知道我可以将其更改为返回 void,只需将参数之一设为 std::array dxdt,但我更喜欢返回值语法,只要没有性能损失或内存泄漏问题。

【问题讨论】:

    标签: c++ arrays return-value


    【解决方案1】:

    我相信您不必担心不必要的复制,因为编译器非常擅长优化这些事情。它可以使用RVO(返回值优化)来消除不必要的副本。如果您有支持 C++11 的编译器,由于 move 语义,复制会进一步减少。

    【讨论】:

    • 在std::array&lt;T,N&gt; 的特殊情况下,移动语义没有什么价值。移动语义仅在存在间接(动态分配的内存和指针)时才有帮助。
    【解决方案2】:

    如果您通过引用返回,则不会进行复制操作(实际上我们只处理 4 个字节,一个内存地址)。

    std::array<double, 4> f(double t, const std::array<double, 4> & x);
    

    ...可以变成:

    std::array<double, 4>& f(double t, const std::array<double, 4> & x);
    

    ...前提是返回的值不是函数本身内的本地堆栈分配变量(因为返回后它将是一个悬空引用)。

    在某些情况下编译器可能会自动执行此操作,但明确说明会更好 - 正如您所说,您希望确保不会发生复制。

    【讨论】:

    • 嗯...这需要一个全局变量来引用。不太好。
    【解决方案3】:

    别担心:这是一个非常常见的用例,编译器很好理解。确保启用优化。还有一件事,如果您的数组是 4 double 长,那么在此之前您可能还有更重要的事情需要优化。不要忘记在你知道你需要它(即你测量)之前进行优化是邪恶的。

    现在,如果您真的想确保没有副本,您可以查看汇编代码(具体如何取决于您的编译器/工具)。

    或者,您可以编写自己的类,在复制构造函数中打印某些内容和赋值运算符以确保它们不被调用,但您将无法做到这一点std::array&lt;&gt; 除非你从它派生这是不允许的(它会编译并运行)。

    【讨论】:

      【解决方案4】:

      这应该是你不应该关心的事情,因为优化副本是一个编译器问题。但是编译器仍然是基于图灵的机器,不能自己发明优化。

      据我所知,当今大多数编译器的行为,如果是一个函数:

      • 只有一个 return 语句或 ...
      • 有多个返回返回所有相同的变量

      编译器将该变量分配到堆栈上,该变量必须由函数调用所属的表达式求值。 不会为此生成任何副本。

      如果你的函数有多个出口返回不同的表达式,这 - 除了一些特殊情况 - 不能完成。

      所以,一般来说,如果你必须返回一些宽对象,只需声明它并确保所有的 return 语句都只返回它。

      【讨论】:

      • 谢谢!我将坚持将函数调用的最后一个参数作为“输出”参数,因为很可能函数调用不会发生在我声明将持有的变量的同一行返回值,这样,我确定没有复制。
      • @hazelnusse:如果你能做到这一点,让返回值成为“输出参数”,那么你所有的返回语句显然可能返回相同的变量。因此,命名的返回值优化将启动。那么......有什么问题?
      • @hazelnusse:“...不会出现在同一行...” 从来没有谈论过“行”-s ...有什么我没有得到的吗?
      • @NicolBolas, @EmilioGaravaglia:假设我有一个 dxdt 的参考,我凑合了dxdt = f(t, x);。 rvo 还会启动吗?
      猜你喜欢
      • 2019-02-19
      • 1970-01-01
      • 2017-10-13
      • 1970-01-01
      • 2018-05-26
      • 2016-09-24
      • 2017-12-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多