Проблема тут полягає в тому, що оскільки клас шаблонується T
, у конструкторі Foo(T&&)
ми не виконуємо виведення типу; У нас завжди є посилання на значення r. Тобто конструктор для Foo
насправді виглядає так:
Foo(int&&)
Foo(2)
працює, тому що 2
є первинним.
Foo(x)
не тому x
, що це значення, яке не може зв'язатись int&&
. Ви можете зробити std::move(x)
це, щоб передати його відповідному типу ( демо )
Foo<int&>(x)
працює просто чудово, оскільки конструктор стає Foo(int&)
завдяки правилам згортання посилань; спочатку це Foo((int&)&&)
згортається Foo(int&)
відповідно до стандарту.
Що стосується вашого "зайвого" посібника з вирахувань: Спочатку існує код за замовчуванням щодо виведення шаблону для коду, який в основному діє як допоміжна функція так:
template<typename T>
struct Foo {
Foo(T&&) {}
};
template<typename T>
Foo<T> MakeFoo(std::add_rvalue_reference_t<T> value)
{
return Foo<T>(std::move(value));
}
//...
auto f = MakeFoo(x);
Це тому, що стандарт диктує, що цей (вигаданий) шаблон шаблону має ті ж параметри шаблону, що і клас (Just T
), за яким слід будь-які параметри шаблону, як і конструктор (жоден в цьому випадку; конструктор не шаблонований). Тоді типи параметрів функції такі ж, як у конструктора. У нашому випадку, після інстанції Foo<int>
, конструктор виглядає як Foo(int&&)
реферат-референс іншими словами. Отже, використання add_rvalue_reference_t
вище.
Очевидно, це не працює.
Коли ви додали "зайвий" посібник щодо відрахувань:
template<typename T>
Foo(T&&) -> Foo<T>;
Ви дозволили компілятору відрізнити, що, незважаючи на будь-які посилання, приєднані до T
конструктора ( int&
, const int&
або int&&
тощо), ви мали намір зробити висновок про тип для класу без посилання (просто T
). Це відбувається тому , що ми раптом будемо виконувати висновок типу.
Тепер ми генеруємо ще одну (вигадану) помічну функцію, яка виглядає приблизно так:
template<class U>
Foo<U> MakeFoo(U&& u)
{
return Foo<U>(std::forward<U>(u));
}
// ...
auto f = MakeFoo(x);
(Наші виклики до конструктора перенаправляються на функцію helper для виведення аргументу шаблону класу, так Foo(x)
стає MakeFoo(x)
).
Це дозволяє U&&
стати int&
і T
стати простоint