2012-01-14 17 views
7

estoy mirando unique_ptr [de VC10] y hacer un par de cosas que no entiendo:¿Cuándo debería usar remove_reference y add_reference?

typedef typename tr1::remove_reference<_Dx>::type _Dx_noref; 

_Dx_noref& get_deleter() 
    { // return reference to deleter 
    return (_Mydel); 
    } 

unique_ptr(pointer _Ptr, 
    typename _If<tr1::is_reference<_Dx>::value, _Dx, 
     const typename tr1::remove_reference<_Dx>::type&>::_Type _Dt) 
    : _Mybase(_Ptr, _Dt) 
    { // construct with pointer and (maybe const) deleter& 
    } 

typename tr1::add_reference<_Ty>::type operator*() const 
    { // return reference to object 
    return (*this->_Myptr); 
    } 

No sería simplemente escribiendo _Dx & o _Ty & ser la misma cosa?

que realmente entienden por qué lo hicieron aquí, sin embargo:

unique_ptr(pointer _Ptr, typename tr1::remove_reference<_Dx>::type&& _Dt) 
    : _Mybase(_Ptr, _STD move(_Dt)) 
    { // construct by moving deleter 
    } 
+5

La respuesta es que necesitas aprender C++, y entonces deberías tener una idea de cómo hacer lo correcto. Lo siento si esto suena duro, pero una vez que tengas una comprensión más firme de las plantillas y la deducción de argumentos, podrás descifrar esto. Pero no tiene sentido saltar al fondo, * especialmente * mirando una implementación real de la biblioteca. Es como tratar de aprender griego antiguo desenterrando artefactos en Persia en lugar de leer un libro de texto. –

+7

@KerrekSB: A veces es mucho más fácil aprender de un solo ejemplo bien explicado que de un libro de texto completo. Además, la mayoría de los libros de texto ni siquiera cubrirán este material, ya que se escribieron referencias rvalues ​​en el idioma. Esta es una pregunta válida y bien formulada, que puedo imaginar que sea útil para mucha gente que está aprendiendo C++. – Mankarse

+1

@KerrekSB Esto no es exactamente algo que pueda buscar. Incluso si sucediera a través de la regla que provocó que el ctor tuviera que hacer lo que hace, probablemente no la asociaría aquí sin ver ejemplos. En otra nota, ¿puedes dejar de disparar todas mis publicaciones? Claramente tienes un problema conmigo (no me importa por qué). – David

Respuesta

14

get_deleter

Cualquier referencia se elimina del tipo de retorno, a continuación, una referencia se vuelve a añadir. En C++ 11 conformes, al agregar un & a un & existente (o &&) se produce un &. Sin embargo, en C++ 03, eso estaría formando una referencia al tipo de referencia, que era ilegal. Probablemente MSVC está usando las reglas anteriores, o ese código se escribió cuando lo hizo y permanece porque es inofensivo.

constructor

Aquí se eliminan las referencias, agregue const, y luego agregar la referencia hacia atrás, a ser pasando por const referencia. Esto es porque agregar const directamente a un tipo de referencia hace nada! (§8.3.2/1) En C++ 11 o C++ 03, la declaración del parámetro sería válida pero no agregaría un const, si la referencia no se eliminó y reemplazó.

operator*

Ésta es esencialmente la misma que get_deleter, pero fue sobre él de una manera diferente, y _Ty no puede ser un tipo de referencia para empezar. Me parece que _Ty& sería suficiente, pero es su prerrogativa.

+1

Awesome breakdown man - gracias. Yo recomendaría varias veces si pudiera, especialmente después de que otros llamasen sin razón aparente. Por cierto, ¿por qué no puede _Ty ser un tipo de referencia para empezar? – David

+0

@Dave: Bueno, '_Ty' * no debe * ser un tipo de referencia porque no puede tener un puntero a una referencia. (8.3.2/5) No estoy verificando si hay alguna peculiaridad que ayude a quitar una referencia en ese caso, pero en caso contrario, el miembro 'unique_ptr' typedef' pointer' estaría mal formado. – Potatoswatter

+0

@Dave: Nadie te llamó la atención; tomar lo que se suponía que era una crítica constructiva tan poco es simplemente una tontería. – ildjarn

2

He aquí un ejemplo, posiblemente arquetípica, por eso que necesitamos remove_reference, en la implementación de std::move: El objetivo es devolver un tipo rvalue referencia, basado en el deducido tipo de argumento de la función.

Ejemplo: Foo x; move(x); Aquí move(x) debe devolver un tipo Foo&&. Pero el argumento de move es una expresión del tipo Foo&. Entonces, ¿cómo puede la función move deducir el tipo correcto?

El primer intento es utilizar la deducción argumento de plantilla ordinaria y utilizar un reparto:

template <typename T> T && move(??? x) { return static_cast<T&&>(x); } 

Pero qué debe entrar ???? Si decimos T x, entonces T se deduce como Foo&; si decimos T & x, luego T = Foo, y si decimos T && x, no coincidirá en absoluto. La segunda versión, T & x, parece ser útil.

Pero entonces la función no funciona en rvalues, para empezar (por ejemplo move(Foo(...)). En este caso, queremos T && x modo que T = Foo y T&& = Foo&& lo deseas. Podríamos tener dos sobrecargas, pero tener múltiples sobrecargas no es deseable porque aumenta la complejidad innecesariamenteY finalmente, si alguien fuera a especificar el parámetro de la plantilla explícitamente como move<Foo&>(x), la función sería nunca funciona, porque cuando T = Foo&, entonces también.

Así que en viene remove_reference:

template <typename T> 
typename std::remove_reference<T>::type && move(T && x) 
{ 
    return static_cast<typename std::remove_reference<T>::type &&>(x); 
} 

En primer lugar, la nueva referencia colapso reglas implican que es T deduce como sea Foo& o Foo&& en los dos casos. Luego, remove_reference quita la referencia y proporciona el tipo Foo en cualquier caso, y al agregar && se obtiene el tipo de devolución Foo&& deseado.

En un resumen muy simplificado: necesitamos remove_reference porque (Foo&)&& es Foo& y no Foo&&. Si alguna vez escribe un código de plantilla que necesita el tipo de base de un parámetro de plantilla que podría deducirse como U& o U&&, puede usar este modelo.

1

template class add_reference tiene especialización para void, const void y const volatile void porque no está permitido referencia de void type (void&). Si se usa _Ty&, generaría un error de compilación cuando _Ty = void.

Cuestiones relacionadas