Ответ 1
В последнем примере arg
является ссылкой rvalue.
Ссылка на пересылку - это ссылка rvalue на параметр cv-unqualified template
и Tuple<Ts...>
не является параметром шаблона.
(Цитата из [temp.deduct.call].)
Различие между ссылками rvalue и ссылками на пересылку было достаточно ясно описано в этом примере Скоттом Мейерсом:
Widget&& var1 = someWidget; // here, "&&" means rvalue reference (1)
auto&& var2 = var1; // here, "&&" does not mean rvalue reference (2)
template<typename T>
void f(std::vector<T>&& param); // here, "&&" means rvalue reference (3)
template<typename T>
void f(T&& param); // here, "&&"does not mean rvalue reference (4)
По существу, различие происходит, когда мы имеем выводимый контекст, поэтому случай (3) явно утверждает, что мы имеем vector<...>&&
, тогда как T
в случае (4) следует вывести и (после применения правил сворачивания ссылок), классифицированных в терминах "категория стоимости".
Но что происходит с более сложным сопоставлением шаблонов? Возьмем следующий пример, например:
template <template <class...> class Tuple, class... Ts>
void f(Tuple<Ts...>&& arg)
{
}
Что означает &&
здесь?
В последнем примере arg
является ссылкой rvalue.
Ссылка на пересылку - это ссылка rvalue на параметр cv-unqualified template
и Tuple<Ts...>
не является параметром шаблона.
(Цитата из [temp.deduct.call].)
Это ссылка rvalue, а не ссылка на пересылку.
Самый простой способ быть уверенным в том, чтобы попытаться передать значение lvalue, если оно терпит неудачу, то это ссылка rvalue, если нет, то ссылка на пересылку:
template<typename... Ts>
struct foo {};
//f function definition
int main() {
foo<int, double> bar;
f(bar); // fails! Cannot bind lvalue to rvalue reference
f(foo<int, double>{}); // ok, rvalue is passed
}
Ссылка на пересылку концепции не является стандартной концепцией, полезно ее распознать, когда вы ее видите, но если вы хотите правильно ее понять и справиться с ней, вы должны понять ссылочную арифметику. (Я считаю, что книга Мейера также гласит об этом)
Что стоит за концепцией ссылки пересылки, является эталонной арифметикой:
Позвольте моделировать вывод типа шаблона компилятора с помощью ссылки пересылки
template<class T>
void foo(T&&);
//...
const int i=42;
foo(i); // the compiler will defines T = const int &
// T&& = const int & && = const int &
// => the compiler instantiates void foo<const int &>(const int &);
foo(6*7);// the compiler will defines T = int
// T&& = int &&
// the compiler instantiates void foo<int>(int &&);
в такой ситуации, создание шаблона foo может производить
функция, которая принимает аргумент с помощью ссылки lvalue или функций, которая принимает аргументы rvalue: ссылка перенаправления либо
ссылку на rvalue или ссылку на lvalue, в зависимости от вычитания типа шаблона. Он называется так, потому что в такой ситуации параметр должен передаваться либо как lvalue, либо как значение x, и это задание T&& std::forward<T>(T&& a)
Если вы заявляете, что функция имеет:
template<class T>
void foo(ATemplateClass<T> && a);
независимо от того, какой тип вывел для T компилятором, вы получите ссылочный параметр rvalue.