Ответ 1
Я думаю, что большая путаница возникает из-за того, что не сообщается, что подразумевается под передачей по ссылке. Когда некоторые люди говорят, что они передаются по ссылке, они обычно означают не сам аргумент, а объект, на который ссылаются. Некоторые другие говорят, что передача по ссылке означает, что объект не может быть изменен в вызываемом. Пример:
struct Object {
int i;
};
void sample(Object* o) { // 1
o->i++;
}
void sample(Object const& o) { // 2
// nothing useful here :)
}
void sample(Object & o) { // 3
o.i++;
}
void sample1(Object o) { // 4
o.i++;
}
int main() {
Object obj = { 10 };
Object const obj_c = { 10 };
sample(&obj); // calls 1
sample(obj) // calls 3
sample(obj_c); // calls 2
sample1(obj); // calls 4
}
Некоторые люди утверждают, что 1 и 3 проходят по ссылке, а 2 - по стоимости. Другая группа людей говорит, что все, кроме последнего, проходят по ссылке, потому что сам объект не копируется.
Я хотел бы сделать определение того, что здесь, как я утверждаю, проходит по ссылке. Общий обзор по нему можно найти здесь: Разница между передачей по ссылке и переходом по значению. Первый и последний проходят по значению, а средние два проходят по ссылке:
sample(&obj);
// yields a 'Object*'. Passes a *pointer* to the object by value.
// The caller can change the pointer (the parameter), but that
// won't change the temporary pointer created on the call side (the argument).
sample(obj)
// passes the object by *reference*. It denotes the object itself. The callee
// has got a reference parameter.
sample(obj_c);
// also passes *by reference*. the reference parameter references the
// same object like the argument expression.
sample1(obj);
// pass by value. The parameter object denotes a different object than the
// one passed in.
Я голосую за следующее определение:
Аргумент (1.3.1) передается по ссылке тогда и только тогда, когда соответствующий параметр вызываемой функции имеет ссылочный тип и ссылочный параметр напрямую связывается с выражением аргумента (8.5.3/4). Во всех остальных случаях мы имеем дело с переходом по значению.
Это означает, что следующее значение передается по значению:
void f1(Object const& o);
f1(Object()); // 1
void f2(int const& i);
f2(42); // 2
void f3(Object o);
f3(Object()); // 3
Object o1; f3(o1); // 4
void f4(Object *o);
Object o1; f4(&o1); // 5
1
- это пропуск по значению, поскольку он не связан напрямую. Реализация может скопировать временную и затем привязать ее к ссылке. 2
передается по значению, потому что реализация инициализирует временный литерал, а затем привязывается к ссылке. 3
передается по значению, поскольку параметр не имеет ссылочного типа. 4
- по значению по той же причине. 5
передается по значению, потому что параметр не имеет ссылочного типа. Следующие случаи проходят по ссылке (по правилам 8.5.3/4 и другие):
void f1(Object *& op);
Object a; Object *op1 = &a; f1(op1); // 1
void f2(Object const& op);
Object b; f2(b); // 2
struct A { };
struct B { operator A&() { static A a; return a; } };
void f3(A &);
B b; f3(b); // passes the static a by reference