Я хочу, чтобы freeFunct делал неконстантные вещи для своей собственной копии объекта a.
Допустим, что freeFunct должен быть свободной функцией
потому что в реальных случаях кода он принимает много разных параметров,
вызывает несколько открытых функций от всех из них, и нет
смысл в том, чтобы сделать ее нестатической функцией-членом любого класса.
Три разных способа объявить это приходят мне на ум.
У меня такое ощущение, что третье решение хуже.
Есть ли разница между первыми двумя?
Есть ли что-то лучше?
void freeFunct1(A a){
a.doStuff();
}
void freeFunct2(const A& a){
A b = a;
b.doStuff();
}
/**users of freeFunct3 are expected
*to give a copy of their variable:
*{
* A b = a;
* freeFunct3(b);
*}
*/
void freeFunct3(A& a){
a.doStuff();
}
Во-первых, как уже говорилось, не делай freeFunct3
если семантика свободной функции состоит в том, чтобы модифицировать только свой «собственный» объект.
Во-вторых, есть различия между freeFunct1
а также freeFunct2
, относящийся к оптимизация хода [C ++, 11], исключительная безопасность, и потенциально размер кода.
С freeFunct2
(принимая по ссылке на const):
A
бросает исключение, он бросит внутри тела функции.A
Копируется конструктор копирования (а функции нет), он будет расширен один раз, внутри тела функции (даже если функция вызывается из нескольких разных мест).С freeFunct1
(принимая по значению):
A
имеет конструктор перемещения, и вы передаете rvalue (например, вызов freeFunct1(A(args))
).A
бросает исключение, он бросит на сайте вызова.A
Конструктор копирования (или перемещения) встроен, он будет расширен многократно, на каждом сайте вызова.В качестве альтернативы вы можете перегрузить ссылку на lvalue / rvalue, чтобы избежать ненужного копирования значений rvalue:
void freeFunct4(const A& a){
A b = a;
b.doStuff();
}
void freeFunct4(A&& a){
a.doStuff();
}
Первый вариант самый лучший: он позволяет вызывающей стороне выбирать, копировать или перемещать свой объект, поэтому он может быть более эффективным, если вызывающей стороне не нужно сохранять копию.
freeFunct1(a); // "a" is copied and not changed
freeFunct1(std::move(a)); // "a" is moved and perhaps changed
Второй похож, но заставляет копию.
Третий, как вы говорите, более подвержен ошибкам, поскольку вызывающий должен знать, что он изменит аргумент.
ИМО, первый самый лучший, а последний самый плохой.
Однако очень немногие люди настолько привыкли передавать по константной ссылке, что по умолчанию пишут # 2, хотя в этом случае им нужна копия, которой она пытается избежать.
Первый меняет только локальную копию. Второй такой же, как первый, но с дополнительным кодом. Третий внесет изменения в a
видимый для абонента freeFunct3
поскольку это неконстантная ссылка. Если вызывается как в комментарии над функцией, то на самом деле она ничем не отличается от второй версии.
Так что, если вы просто хотите изменить локальную копию без передачи этих изменений вызывающей стороне, то я рекомендую первую версию.