Между ними есть разница. Учтите следующее:
#include <iostream>
#include <string>
using std::string;
string g_value;
void callback() {
g_value = "blue";
}
void ProcessStringByRef(const string &s) {
callback();
std::cout << s << "\n";
}
void ProcessStringByValue(const string s) {
callback();
std::cout << s << "\n";
}
int main() {
g_value = "red";
ProcessStringByValue(g_value);
g_value = "red";
ProcessStringByRef(g_value);
}
Выход:
red
blue
То, что ссылка является константой внутри функции, не означает, что ссылка не может быть изменена с помощью других ссылок (ситуация, когда один объект имеет несколько ссылок или указателей на него, называется «псевдонимом»). Таким образом, существует различие между передачей константной ссылки и передачей константного значения - в случае ссылки объект может измениться после выполнения вызова. В случае значения вызываемый объект имеет личную копию, которая не изменится.
Поскольку они делают разные вещи, C ++ позволяет вам выбирать, что вы хотите.
В любом случае для производительности есть последствия - когда вы передаете по значению, должна быть сделана копия, которая стоит. Но тогда компилятор знает, что только ваша функция может иметь какие-либо ссылки на эту копию, что может позволить другие оптимизации. ProcessStringByRef не может загрузить содержимое строки для печати, пока не вернется callback()
. ProcessStringByValue может, если компилятор считает, что это происходит быстрее.
Обычно вы заботитесь о копии, а не о порядке выполнения инструкций, потому что обычно копия намного дороже. Поэтому обычно вы передаете ссылки, где это возможно, для объектов, которые нетривиально копировать. Но возможность наложения псевдонимов иногда имеет действительно серьезные последствия для производительности, поскольку предотвращает определенную оптимизацию, даже если на самом деле псевдонимов не происходит. Вот почему существуют "строгие правила наложения имен", а ключевое слово restrict
в C99.