Як @ JDługosz в коментарях зазначає, Герб дає інші поради в іншій (пізніше?) Розмові, дивіться приблизно звідси: https://youtu.be/xnqTKD8uD64?t=54m50s .
Його порада зводиться лише до використання параметрів значення для функції, fяка приймає так звані аргументи занурення, припускаючи, що ви перемістите конструкцію з цих аргументів раковини.
Цей загальний підхід лише додає накладні витрати конструктора руху для аргументів lvalue та rvalue порівняно з оптимальною реалізацією fвідповідно до аргументів lvalue та rvalue. Щоб зрозуміти, чому це так, припустимо, fприймає параметр значення, де Tзнаходиться деяка копія та переміщення сконструйованого типу:
void f(T x) {
T y{std::move(x)};
}
Виклик fаргументом lvalue призведе до того, що конструктор копії буде викликаний для побудови x, а конструктор переміщення буде викликаний для побудови y. З іншого боку, виклик fз аргументом rvalue викликає конструктор переміщення, який буде викликаний для побудови x, а інший конструктор переміщення буде викликаний для побудови y.
Загалом, оптимальна реалізація fаргументів lvalue полягає в наступному:
void f(const T& x) {
T y{x};
}
У цьому випадку для конструювання викликається лише один конструктор копій y. Оптимальною реалізацією fаргументів для rvalue є, знову ж таки, загалом таке:
void f(T&& x) {
T y{std::move(x)};
}
У цьому випадку для побудови викликається лише один конструктор переміщення y.
Отже, розумний компроміс - це прийняти параметр значення та мати додатковий виклик конструктора ходу для аргументів lvalue або rvalue щодо оптимальної реалізації, що також є порадою, наданою в розмові Herb.
Як в коментарях зазначав @ JDługosz, передача значення має сенс лише для функцій, які будуватимуть якийсь об'єкт з аргументу потоку. Якщо у вас є функція, fяка копіює її аргумент, підхід "передача за значенням" матиме більше накладних витрат, ніж загальний підхід "по базі". Підхід передачі значення для функції, fщо зберігає копію свого параметра, матиме вигляд:
void f(T x) {
T y{...};
...
y = std::move(x);
}
У цьому випадку є аргумент копії та призначення переміщення для аргументу lvalue, а також переміщення конструкції та призначення переміщення для аргументу rvalue. Найбільш оптимальним випадком аргументу lvalue є:
void f(const T& x) {
T y{...};
...
y = x;
}
Це зводиться лише до призначення, що потенційно набагато дешевше, ніж конструктор копій плюс присвоєння переміщення, необхідний для підходу прохідної вартості. Причиною цього є те, що присвоєння може повторно використовувати наявну виділену пам'ять у y, а отже, запобігати (де) виділенням, тоді як конструктор копій зазвичай виділяє пам'ять.
Для аргументу rvalue найоптимальнішою реалізацією для fзбереження копії є така форма:
void f(T&& x) {
T y{...};
...
y = std::move(x);
}
Отже, лише призначення руху в цьому випадку. Передача rvalue до версії, fяка має посилання const, коштує лише призначення замість призначення переміщення. Таким чином, відносно кажучи, fпереважнішим є варіант прийняття посилання в цьому випадку як загальної реалізації.
Тож загалом для найбільш оптимальної реалізації вам потрібно буде перевантажувати або робити якесь ідеальне переадресація, як показано в бесіді. Недолік - це комбінаторний вибух у кількості необхідних перевантажень, залежно від кількості параметрів, fякщо ви вирішите перевантажувати категорію значень аргументу. Ідеальне переадресація має недолік, який fстає функцією шаблону, що перешкоджає зробити його віртуальним, і приводить до значно складнішого коду, якщо ви хочете отримати його на 100% правильно (див. Розмову про деталі горі).