Проблема тут полягає в тому, що оскільки клас шаблонується 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