C++ 通过常量引用传递和通过常量引用返回
Posted
技术标签:
【中文标题】C++ 通过常量引用传递和通过常量引用返回【英文标题】:C++ Pass By Const Reference and Return By Const Reference 【发布时间】:2011-03-14 02:47:23 【问题描述】:我试图了解返回 const
引用是否有任何好处。我有一个通常看起来像这样的阶乘函数:
unsigned long factorial(unsigned long n)
return (n == 0) ? 1 : n * factorial(n - 1);
我假设当我们通过const
引用并返回const
引用时性能会有所提高...但是const
-正确性总是让我感到困惑。
const unsigned long & factorial(const unsigned long& n)
return (n == 0) ? 1 : n * factorial(n - 1);
返回const
引用是否有效?此外,有人可以告诉我:它有益吗?
【问题讨论】:
当返回值超出范围(即在函数末尾)时会发生什么?我的猜测是引用指向无效内存。 通过 const 引用传递long
不太可能有利于按值传递它。一般来说,这是特定于实现的,但在我能想到的所有架构上,long
和 long&
都将表示为 32 位数量,或者两者都表示为 64 位。因此,您复制了相同数量的数据,但现在调用者必须计算地址(这意味着它还必须有一个本地地址,而不仅仅是保存在寄存器中),而被调用者必须取消引用。充其量,函数会被内联,优化器会删除引用。
所以如果我只将返回和输入参数设为 const (没有引用,只有 const 值),我是安全的。
你很安全,但它也毫无意义(至少对调用者而言)——因为“***”const
完全从函数签名的参数中删除,并且将保留但没有任何区别对于 long
的返回类型(尽管它可能对类类型有所不同)。
@robinjam 我认为这种行为是“未定义的”(参见 en.wikipedia.org/wiki/Undefined_behavior 和 catb.org/jargon/html/N/nasal-demons.html )。使用 MSVC,它将指向现在正用于其他用途的有效(堆栈)内存(或至少,不再为局部变量保留),即内存是可读的,但可能包含一个随机数(或者,可能 偶尔包含一个随机数,具体取决于是否发生中断并用其他内容覆盖内存位置)。
【参考方案1】:
这是无效的。您不能返回对局部变量的引用。
MSVS C++ 编译器甚至给出以下警告:
main.cc : warning C4172: returning address of local variable or temporary
不太确定 GCC,但结果可能是一样的。
【讨论】:
好的,但是可以安全地假设有效的优化是使输入参数为 const 引用,因为我听说通过引用传递 const 对内置类型没有太大好处(如长)。而且我至少应该将返回类型设为 const(没有引用)? 经验法则是:当您知道类型时 - 按值传递基元,按值传递封装单个基元的类,通过 const 引用传递其他所有内容。对于不透明的第三方类,告诉他们的文档告诉你做什么(例如迭代器 - 按值传递)。当您事先不知道类型时(例如,它是模板类型参数),通过 const 引用传递并希望优化器将其剥离到不需要的地方:) @Lirik 我个人只在使用const
引用时使用const
,并且只在类中使用。否则,它们根本没有意义。关于优化 - 优化和配置文件。此处的引用(在汇编代码中)有时会使事情变得更好,有时会变得更糟。您可能也对此感兴趣:cpp-next.com/archive/2009/08/want-speed-pass-by-value【参考方案2】:
这里的 const 引用不正确 - 您正在返回对局部变量的引用 - 这里是一个未命名的临时变量,1
或 n * factorial(n - 1)
的结果。因为引用是对函数中的一个局部变量,所以当引用到达调用者时,该局部变量已经超出范围,并且是无效的。
返回对大型结构化类型的 const 引用,当该引用在函数退出后仍然存在时,您希望避免复制到该引用。通常,这意味着返回对参数或成员变量的引用(在类的情况下)。
【讨论】:
【参考方案3】:如果值的大小很小,则 const 引用不会比值快。在这种情况下,值的类型是long
,它是 IMO 小(例如 4 到 8 个字节):所以 const 引用不会更快。事实上它可能更慢,因为要获取引用的值,编译器可能需要发出将取消引用引用的代码(如取消引用指针)。
鉴于引用(内部)像指针一样实现,我希望当值的大小大于指针的大小时(假设它是偶数),传递引用比传递值获得更好的性能传递引用合法:对超出范围的局部变量的引用是不合法的)。
【讨论】:
对内部变量的指针/引用 is 在表达式的生命周期内有效,这就是 std::string 的 .c_str() 和 operator= 有效的原因。这也是为什么 const char* monkey = myStr.c_str();然后在代码的其他地方使用指针“猴子”是个坏主意。【参考方案4】:可能,但我永远不会那样做。为什么?
因为这是使您的代码不可读的好方法。
此外,编译器将优化它,而无需此 hack。
此外,您的程序没有其他“瓶颈”需要优化吗? 我的意思是,如果您将这部分代码放在汇编中查看它,您会发现将值传递给函数并获得结果只是几个操作码。怎么样? 好吧,一个 32 位整数将适合寄存器。快如“mov eax, ...”。 另一方面,您的程序可能还有其他可能需要优化的设计/算法问题......除非它像“hello world”程序一样简单。
所以,我不会做这些事情,欢迎大家挑战我。
【讨论】:
+1 表示“使代码不可读的好方法”,并且真正破坏了 IMO。我从事过这样的项目,相信我这会造成痛苦:-( 这个问题清楚地表明它不是关于程序代码优化(或可读性),而是关于 理解 const 引用及其用途。如果没有证据,我不相信“编译器无论如何都会优化它”。关于可读性你是对的——但有时你必须做你必须做的事,尤其是在像 C++ 这样的语言中。【参考方案5】:通过 const 引用返回有效的唯一情况是,如果您返回的对象的寿命将超过函数调用*(例如返回调用该函数的类的成员),甚至在这种情况下在上下文中,是否应该这样做是值得怀疑的,因为这将允许两个不同的调用者访问相同的内存位置,如果您使用多个线程,这会使事情成为一场噩梦。
*注意:在您的情况下,您返回的项目是一个局部变量,因此不会超过函数调用。因此,您提供的代码正在调用邪恶的未定义行为。
【讨论】:
以上是关于C++ 通过常量引用传递和通过常量引用返回的主要内容,如果未能解决你的问题,请参考以下文章