Ответ 1
За исключением того, что, когда hold_this() задано lvalue, держатель будет удерживать ссылку, а когда задано значение r, владелец сделает копию?
Вы уже написали (минус требуемый template <typename T>
). Правила вычета для ссылки на пересылку сохраняют следующую категорию:
- Если
t
привязано к lvalue типаT2
, тоT = T2&
. - Если
t
связано с r значением типаT2
, тоT = T2
.
Это те правила вычета, которые std::forward
полагается на выполнение своей работы. И зачем нам также передавать этот тип.
Вышеупомянутое означает, что вы создаете экземпляр holder
непосредственно с помощью T2
в случае rvalue. Давать вам именно то, что вы хотите. Выполняется копия.
На самом деле делаются две копии. Один раз, чтобы создать аргумент конструктора t
, а другая копия - инициализировать из него obj_m
. Но мы можем избавиться от него с помощью умного использования type_traits:
template <class T>
class holder {
T obj_m; // should be a reference if possible...
public:
holder(std::add_rvalue_reference_t<T> t) :obj_m { std::forward<T>(t) } {}
};
template<typename T>
auto hold_this(T && t) { return holder<T>(std::forward<T>(t)); }
Посмотрите в прямом эфире. Мы используем add_rvalue_reference_t
, чтобы сделать t
правильным типом ссылки в каждом случае. И "имитируйте" вывод аргумента, который сделает obj_m { std::forward<T>(t) }
разрешено инициализировать obj_m
из правильного ссылочного типа.
Я говорю "имитировать", потому что важно понять аргумент конструктора для holder
, не может быть ссылкой пересылки, потому что сам конструктор не является шаблоном.
Кстати, поскольку вы отметили С++ 17, мы также можем добавить руководство для выписки на ваш пример. Если мы определим его следующим образом (с обратной связью от T.C.):
template <class T>
class holder {
T obj_m; // should be a reference if possible...
public:
holder(T&& t) :obj_m { std::forward<T>(t) } {}
};
template<typename T>
holder(T&&) -> holder<T>;
Затем этот живой пример показывает, что вы можете определить переменные как hold h1{t};
и hold h2{test()};
, с теми же выводимыми типами, что и функция return значения из ранее.