Проблема здесь в том, что, поскольку класс является шаблоном T
, в конструкторе Foo(T&&)
мы не выполняем дедукцию типа; У нас всегда есть ссылка на r-значение. То есть конструктор для Foo
фактически выглядит так:
Foo(int&&)
Foo(2)
работает, потому что 2
это prvalue.
Foo(x)
не потому, что x
является lvalue, который не может связываться с 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&&)
rvalue-ссылка другими словами. Следовательно использование 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);
(Наши вызовы конструктора перенаправляются во вспомогательную функцию для целей вывода аргументов шаблона класса, поэтому это Foo(x)
делается MakeFoo(x)
).
Это позволяет U&&
стать int&
и T
стать простоint