В чём разница между следующими двумя способами передачи параметров?
template <typename ...Args> smth(Args &&...args) : base(args...) {}
template <typename ...Args> smth(Args &&...args) : base(std::forward<Args>(args)...) {}
Предположим, что конструктор base принимает move-only параметр по значению:
struct base {
base(std::unique_ptr<int> value);
};
В таком случае первый вариант не скомпилируется:
template <typename ...Args> smth(Args &&...args) : base(args...) {}
попытка сделать копию ^^^^
Тут можно написать std::move и это будет работать:
template <typename ...Args> smth(Args &&...args) : base(std::move(args)...) {}
Однако в случае если у base два конструктора, один из которых перемещает значение, а другой принимает ссылку:
struct base {
base(std::unique_ptr<int>&& value);
base(std::unique_ptr<int>& value);
};
И если мы хотим чтобы smth передавал в base именно те типы аргументов, с которыми его вызвали, то std::move нам не подойдет - он всегда будет превращать аргумент в r-value ссылку.
Для этого есть std::forward<T>, который сохраняет оригинальный тип аргумента:
struct smth : base {
template <typename ...Args> smth(Args &&...args) : base(std::forward<Args>(args)...) {}
};
std::unique_ptr<int> p;
smth s1(p); // OK, будет вызван base(std::unique_ptr<int>&);
smth s2(st::move(p)); // OK, будет вызван base(std::unique_ptr<int>&&);
Примечание: std::forward<T>(t) работает только с "универсальнами ссылками", т.е. когда T - это параметр шаблона функции (конструктора), а t - аргумент этой функции с типом T&& (это специальное исключение в языке, такой тип T&& не является r-value ссылкой, а заставляет T повторять тип того, что передали в функцию).
Сборка персонального компьютера от Artline: умный выбор для современных пользователей