【发布时间】:2022-01-21 03:15:42
【问题描述】:
我一直在寻找这个问题的答案,但我似乎找不到任何东西,所以我在这里问:
引用参数是否会衰减为逻辑上需要的指针?
让我解释一下我的意思:
如果我声明一个引用 int 作为参数的函数:
void sum(int& a, const int& b) { a += b; }
(假设这不会被内联)
逻辑假设是调用这个函数可以通过不传递任何参数来优化,而是让函数访问已经在堆栈上的变量。直接更改这些可以防止传递指针的需要。
问题在于(再次假设 this 没有内联),如果从大量不同的位置调用函数,则每次调用的相关值可能位于堆栈中的不同位置,这意味着调用无法优化。
这是否意味着,在这些情况下(如果从代码中的大量不同位置调用函数,这可能构成大多数情况),引用衰减为指针,该指针被传递给函数和用于影响外部范围内的变量?
额外问题:如果这是真的,那是否意味着我应该考虑在函数体中缓存引用的参数,以便避免传递这些引用时隐藏的取消引用?然后我会保守地访问实际的参考参数,只有当我需要实际向它们写一些东西时。如果编译器认为取消引用的成本高于复制一次的成本,这种方法是否有必要,或者最好相信编译器会为我缓存这些值?
奖金问题代码:
void sum(int& a, const int& b) {
int aCached = a;
// Do processing that required reading from a with aCached.
// Do processing the requires writing to a with the a reference.
a += b;
}
额外的问题:假设是否安全(假设以上所有内容都是正确的),当传递“const int& b”时,编译器将足够聪明,可以在通过指针传递时按值传递 b 效率不高足够的?我的理由是“const int& b”的值是可以的,因为你从不尝试写入它,只读取。
【问题讨论】:
-
每个问题只有一个问题。底线是非常简单的IMO。编译器通常比您在优化方面要好得多。如果您需要比较不同的方法,您应该对它们进行分析。猜测或推理编译器可能在内部做什么很可能不会给你任何帮助。此外,就像您在问题中所做的那样,将一堆假设叠加在一起,这也表明您正在以错误的方式思考这个问题。
-
在某些情况下,引用传递是否会衰减为指针传递? 不,不是在 C++ 抽象机中。编译器可能使用指针作为实现细节,或者它可能使用 as-if 规则,或者它可能使用别名,或者它可能使用寄存器......它不是以编程方式公开的。
-
引用传递通常使用对象的地址作为管理引用的方式;编译器知道这一点,并生成代码来处理对引用的读取和写入。如果您认为将地址传递为“衰减为传递指针”,那么,是的,引用衰减为传递指针。但这不是一个有用的特征,因为它混合了语义和实现细节。无论编译器如何实现引用,您可以使用引用执行的操作(如果可以使用指针执行)在源代码中的编写方式不同。
-
将一些测试用例放在一起(与您的实际问题相匹配)并使用在线编译器(例如godbolt.org)来检查生成的代码。例如godbolt.org/z/TMnd4WjWT [注意for clang
sum永远不会被调用。] -
关于附加问题:即使
b没有被sum函数改变,这并不意味着b永远不会改变。例如,某些代码可能会调用它sum(x, x);