c++ - ¿Es esta una referencia de reenvío?
c++11 perfect-forwarding (3)
El concepto de referencia de reenvío no es un concepto estándar, es útil reconocerlo cuando lo vea, pero si quiere entenderlo y tratarlo correctamente, debe entender la aritmética de referencia. (Creo que el libro de Meyer también tiene un capítulo al respecto)
Lo que está detrás del concepto de una referencia de reenvío es la aritmética de referencia:
- && && = &&
- && & = &
- & && = &
- & & = &
Simulemos la deducción del tipo de plantilla del compilador con una referencia de reenvío
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 &&);
en tal situación, la creación de instancias de la plantilla foo puede producir una función que toma el argumento por la referencia lvalue o las funciones que toma la referencia rvalue de los argumentos: una referencia de reenvío es una referencia rvalue o una referencia lvalue, dependiendo de la deducción del tipo de plantilla. Se nombra así, porque en tal situación, el parámetro se pasará como un valor de l o como un valor de x, y este es el trabajo de T&& std::forward<T>(T&& a)
Si declara que una función tiene:
template<class T>
void foo(ATemplateClass<T> && a);
cualquiera que sea el tipo deducido para T por el compilador, obtendrá un parámetro de referencia de valor de r.
Scott Meyers aclaró suficientemente la distinción entre las referencias de valor y las referencias de reenvío:
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)
Esencialmente, la distinción se produce cuando tenemos un contexto deducible , por lo que el caso (3) declara explícitamente que tenemos un vector<...>&&
mientras que la T
en el caso (4) debe deducirse y (después de aplicar las reglas de colapso de referencia) categorizarse en términos de "categoría de valor".
Pero, ¿qué pasa con un patrón de coincidencia un poco más complejo? Tomemos como ejemplo el siguiente caso:
template <template <class...> class Tuple, class... Ts>
void f(Tuple<Ts...>&& arg)
{
}
¿Qué significa &&
aquí?
En tu último ejemplo, arg
es una referencia rvalue.
Una referencia de reenvío es una referencia rvalue a un parámetro de plantilla no calificada cv
y Tuple<Ts...>
no es un parámetro de plantilla.
(Cita de [temp.deduct.call].)
Es una referencia de valor, no una referencia de reenvío.
La forma más fácil de estar seguro es tratar de pasar un lvalue, si falla, entonces es una referencia de rvalue, si no, entonces una referencia de reenvío:
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
}