【问题标题】:Passing integers as constant references versus copying将整数作为常量引用与复制
【发布时间】:2010-06-09 20:12:51
【问题描述】:

这可能是一个愚蠢的问题,但我注意到在大量 API 中,许多采用不打算修改的整数参数的方法签名看起来像:

void method(int x);

而不是:

void method(const int &x);

在我看来,这两者的功能似乎完全相同。 (编辑:在某些情况下显然不是,请参阅 R Samuel Klatchko 的回答)在前者中,该值被复制,因此无法更改原始值。在后者中,传递了一个常量引用,所以不能改变原来的。

我想知道的是为什么一个优于另一个 - 是因为性能与前者基本相同甚至更好吗?例如传递 16 位值或 32 位值而不是 32 位或 64 位地址?这是我能想到的唯一合乎逻辑的原因,我只想知道这是否正确,如果不正确,为什么以及何时应该更喜欢 int x 而不是 const int &x,反之亦然。

【问题讨论】:

  • 所有很好的答案,谢谢大家。因此,作为一般规则,您会将副本传递给 byte/int/bool 等原始类型,并对其他所有内容(类/结构)使用指针和引用? @Jen 我会接受你的回答,因为你是第一个。
  • 是的,这是一般规则,除非有充分的理由不遵守它。例如,如果您要对非原始参数做的第一件事是制作本地副本,以便您可以在不影响调用站点的情况下对其进行修改,那么您不妨按值获取参数。

标签: c++


【解决方案1】:

这不仅仅是传递指针的成本(本质上就是引用),还包括在被调用方法的主体中取消引用以检索底层值。

这就是为什么通过值传递 int 几乎可以保证更快(此外,编译器可以优化并简单地通过处理器寄存器传递 int,无需将其推入堆栈)。

【讨论】:

    【解决方案2】:

    在我看来,这两者的功能完全一样。

    这取决于引用的具体内容。这是一个公认的虚构示例,它会根据您传递引用还是值而改变:

    static int global_value = 0;
    
    int doit(int x)
    {
        ++global_value;
        return x + 1;
    }
    
    int main()
    {
        return doit(global_value);
    }
    

    根据您使用的是int doit(int) 还是int doit(const int &),此代码的行为会有所不同

    【讨论】:

    • 棘手...我从来没有想过这样的案例,谢谢发布。
    • 还有一个风险是该函数可能会保留对值的非常量引用,而不是副本。
    【解决方案3】:

    整数通常是处理器本机字的大小,可以轻松传递到寄存器中。从这个角度来看,值传递和常量引用传递没有区别。

    如有疑问,请打印函数的汇编语言列表,以了解编译器如何传递参数。打印出值传递和常量引用传递。

    另外,当传值时,函数可以修改副本。当通过常量引用传递时,函数不能修改变量(它被标记为const)。

    【讨论】:

      【解决方案4】:

      对于通过引用传递可能会有非常非常小的反优化,因为至少需要进行一次取消引用才能获得实际值(除非调用是内联的,否则编译器不能简单地传递值由于调用站点和函数可能是单独编译的,并且对于实际上不是const 本身的传递参数丢弃const 是有效且明确定义的 - 请参阅What are the benefits to passing integral types by const ref)。但请注意,“去优化”可能太小以至于难以衡量。

      因此,大多数人似乎不喜欢 pass-by-const-ref 的内置函数(有些人非常喜欢)。但是,我认为在某些情况下,如果您希望编译器帮助您确保函数中的值不会被意外更改,它可能会更可取。这不是什么大事,但有时可能会有所帮助。

      【讨论】:

      • 关于防止意外更改的好点,虽然我不完全明白为什么这会有用,因为否则你只会修改副本。
      • 按值传递可确保原始文件不会被更改,就像按常量引用一样。
      • 当你谈论巨大的数据量(例如dsp处理)时,去优化可能会成为一个问题
      • 如果你担心在 body 中意外改变它,即使这不会影响调用者,只需将其声明为 const 值
      • 有几点 - const 只是有助于防止意外,就像 const 的许多其他用途一样(也可以应用于参数的按值传递版本,正如 Chris Dodd 所提到的)。我的主要观点(在二读时非常模糊)是在 99% 的情况下它真的无关紧要。在重要的地方,无论如何都是按值传递。但可能还有其他更紧迫的事情需要担心。比如大括号的位置,或者成员是否应该在名称前加上m_
      【解决方案5】:
      • 根据底层指令集,整数参数可以作为寄存器或堆栈传递。寄存器肯定比内存访问快,这在 const refs 的情况下总是需要的(考虑早期的无缓存架构)

      • 您不能将 int 文字作为 const int& 传递

      • 显式类型转换允许您将 const int& 转换为 * (const int *),从而可以更改传递的引用的值

      【讨论】:

      • 您绝对可以将整数文字作为 const 引用传递。常量引用可以采用右值,包括任何类型的文字。我不知道您的第三点是什么意思;如果你愿意,C++ 会让你将整数转换成大象。
      猜你喜欢
      • 2011-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多