C++中大多数/所有 setter 函数的参数是否应该写为常量引用?

Should the parameters of most/all setter functions in C++ be written as const references?

本文关键字:常量 引用 是否 参数 大多数 所有 setter 函数 C++      更新时间:2023-10-16

例如

void SetNumbers(const double& a, const double& b)
{
m_a = a;
m_b = b
}

而不仅仅是

void SetNumbers(double a, double b) 
{
m_a = a;
m_b = b
}

我听说它更快更安全,也许还有其他优点

在关联类型的复制/移动运算符不平凡的情况下,这两种方法并不等效。

假设你不关心这种区别(即,包括任何复制/移动在内的整个函数调用的效果在任何一种情况下都是相同的(,那么一般来说,这取决于 ABI 的机械细节:

  • 小型类型可以有效地传递,通常在单个寄存器中传递。在这种情况下,没有按值传递的开销,因为副本隐式存在于 ABI 中。
  • 无法在寄存器中传递的较大类型必须通过指针传递,即使"按值传递"也是如此,这意味着在按值传递的情况下会产生副本。
  • 但是,如果函数是内联的,则此副本可能会消失,因为编译器可以看到该副本永远不会被访问。
  • 按值传递可确保参数不能别名,这可能有助于更有效地优化函数,并且此效果有时可能非常大(可以使用__restrict等扩展来获得相同的引用效果(。
  • 内联可以再次"否定"上述优势。

所以不幸的是,没有简单的答案。

仅考虑性能,合理的启发式可能是:

对于将在重要平台上的寄存器中传递的类型,首选按值传递。如果您不知道哪些类型将按值传递,则可以假设只有基元类型按值传递(但这通常是一个悲观的假设(。

对于创建副本的较大类型(如示例中所示(,对于 C++11 及更高版本可能首选按值传递,因为您可以在赋值中std::move参数,对于支持移动的类型,当使用右值调用函数时,参数通常同样有效。对于不可移动的类型或当参数不是右值时,这两种方法都同样有效。

对于较大的类型,其中没有创建副本,我建议通过const引用传递,除非在极少数情况下无法避免别名问题,在这种情况下需要副本。

这取决于参数类型。

如果值的大小是<= sizeof(void*),比如intfloat(通常是double(,最好按值传递它,这样它可能会被放入寄存器中,而不是在内存中查找。

如果它是一个大对象(如数组或字符串(,如果您打算从中复制数据,最好将其作为const &(const 引用(。如果要移动或使用输入,最好将其作为&&(右值引用(接收。