c++ - std:: remove_reference explicado?
templates c++11 (2)
Vi posibles implementaciones para std::remove_reference
como a continuación
template< class T > struct remove_reference {typedef T type;};
template< class T > struct remove_reference<T&> {typedef T type;};
template< class T > struct remove_reference<T&&> {typedef T type;};
¿Por qué es que hay especializaciones para rvalue reference
y rvalue reference
? ¿No será suficiente la plantilla general en sí misma y eliminar la referencia? Estoy confundido aquí porque en la especialización de T&
o T&&
si trato de usar ::type
todavía debería obtener T&
o T&&
respectivamente, ¿no?
¿Podría explicar cómo, por qué lanzamos a remove_reference<t>::type&&
in move? (¿se debe a que el parámetro se nombra para que se trate como un lvalor dentro de la función de movimiento?).
Además, ¿podría señalar una forma en la que pueda averiguar e imprimir cuál es el tipo? ¿Por ejemplo, si es un rvalue
de tipo int
entonces debería poder imprimir que int&&
fue aprobado? (He estado usando std::is_same
para verificar pero manualmente).
Gracias por tu tiempo.
¿Por qué es que hay especializaciones para lvalue y rvalue reference?
Si solo existiera la plantilla primaria, entonces haciendo:
remove_reference<int&>::type
Te daria
int&
Y haciendo:
remove_reference<int&&>::type
Te daria
int&&
Que no es lo que quieres. Las especializaciones para las referencias lvalue y rvalue permiten eliminar el &
y el &&
, respectivamente, del argumento de tipo que se pasa.
Por ejemplo, si estás haciendo:
remove_reference<int&&>
El tipo int&&
coincidirá con el patrón especificado por la especialización de T&&
, siendo T
int
. Dado que la especialización define el tipo de alias de type
como T
(en este caso, int
), se hace:
remove_reference<int&&>::type
Te daré int
.
¿Podría explicar cómo, por qué lanzamos a
remove_reference<t>::type&&
inmove
?
Eso es porque si move()
se definió de la siguiente manera:
template<typename T>
T&& move(T&& t) { ... }
// ^^^
// Resolves to X& if T is X& (which is the case if the input has type X
// and is an lvalue)
Entonces, el tipo de retorno será X&
si el argumento de move()
es un lvalue de tipo X
(así es como las llamadas "referencias universales"). Queremos asegurarnos de que el tipo de retorno sea siempre una referencia rvalue.
El propósito de move()
es devolverte un valor, sin importar lo que pases en la entrada. Dado que una llamada de función para una función cuyo tipo de retorno es una referencia de rvalue es un valor de r, realmente queremos que move()
devuelva siempre una referencia de rvalue.
Esa es la razón por la que remove_reference<T>::type&&
, ya que anexar &&
a un tipo que no es de referencia siempre se garantiza que produzca un tipo de referencia de valor nominal.
¿También podría indicar una forma en la que puedo averiguar e imprimir cuál es el tipo?
No estoy seguro de lo que quiere decir con "imprimir" aquí. No hay una forma portátil que conozca de convertir el nombre de un tipo en una cadena (sin importar cómo obtenga ese tipo).
Si su objetivo es asegurarse de que se haya pasado un rvalor, por otro lado, podría usar una aserción estática así:
#include <type_traits>
template<typename T>
void foo(T&&)
{
static_assert(!std::is_reference<T>::value, "Error: lvalue was passed!");
// ...
}
Lo que se basa en el hecho de que cuando se pasa un lvalue de tipo X
, se deducirá T
como X&
.
También puede usar una restricción SFINAE equivalente, si solo desea producir un fallo de sustitución:
#include <type_traits>
template<typename T, typename std::enable_if<
!std::is_reference<T>::value>::type* = nullptr>
void foo(T&&)
{
// ...
}
Cuando trata algún tipo de parámetro de plantilla, el compilador busca la especialización más "especializada". Si pasa un int && a esta plantilla, el compilador usa la remove_reference<T&&>
. La especialización general no le brinda lo que usted desea: si pasa int&&
a la especialización general, el tipo será int&&
Si desea imprimir el tipo, use typeid(some_type).name()