Ссылки не присутствовали в C. Однако, C действительно имел то, что равносильно изменяемым аргументам, передаваемым по ссылке. Пример:
int foo (int in, int * out) {return (* out) ++ + in; }
// ...
int x = 1; int y = 2;
x = foo (x, & y);
// x == y == 3.
Однако было распространенной ошибкой забывать разыменовывать «out» при каждом использовании в более сложных функциях foo (). Ссылки на C ++ допускают более плавный синтаксис для представления изменяемых членов замыкания. В обоих языках это может мешать оптимизации компилятора, поскольку несколько символов ссылаются на одно и то же хранилище. (Рассмотрим «foo (x, x)». Теперь не определено, встречается ли «++» после только «* out» или также после «in», поскольку между этими двумя точками нет последовательности, а приращение требуется только для произойдет когда-нибудь после того, как будет взято значение левого выражения.)
Но кроме того, явные ссылки устраняют неоднозначность в двух случаях для компилятора C ++. Указатель, передаваемый в функцию C, может быть изменяемым аргументом или указателем на массив (или многое другое, но эти два адекватно иллюстрируют неоднозначность). Контраст "char * x" и "char * y". (... или не сделать этого, как ожидалось.) Переменная, переданная по ссылке в функцию C ++, однозначно является изменяемым членом замыкания. Если, например, у нас было
// в классе baz
private: int bar (int & x, int & y) {return x - y};
public: int foo (int & x, int & y) {return x + bar (x, y);}
// выход из области действия и блуждание по ...
int a = 1; int b = 2; баз с;
a = c.foo (a, b);
Мы знаем несколько вещей:
bar () вызывается только из foo (). Это означает, что bar () можно скомпилировать так, чтобы два его аргумента находились в кадре стека foo (), а не в его собственном. Это называется copy elision, и это отличная вещь.
Копирование elision становится еще более захватывающим, когда функция имеет вид "T & foo (T &)", компилятор знает, что временное входящее и выходящее время, и компилятор может сделать вывод, что результат может быть создан на месте аргумента. Тогда нет необходимости компилировать временные входные или выходные данные. Foo () может быть скомпилирован, чтобы получить свой аргумент из некоторого вмещающего фрейма стека и записать свой результат непосредственно в какой-либо вмещающий фрейм стека.
недавняя статья о копировании, и (удивительно) она работает еще лучше, если вы передаете по значению в современных компиляторах (и как ссылки на rvalue в C ++ 0x помогут компиляторам пропускать еще больше бессмысленных копий ) см. http://cpp -next.com / archive / 2009/08 / want-speed-pass-by-value / .