2011-10-05 8 views
9

Tengo el siguiente fragmento de código, como un ejemplo dec_proxy intenta revertir los efectos del operador de incremento sobre el tipo que se ejecuta en una llamada de función compleja foo - que por cierto no puedo cambiar la interfaz de.Pasando temporarios como referencias no-const en C++

#include <iostream> 

template<typename T> 
class dec_proxy 
{ 
public: 
    dec_proxy(T& t) 
    :t_(t) 
    {} 

    dec_proxy<T>& operator++() 
    { 
     --t_; 
     return *this; 
    } 

private: 
    T& t_; 
}; 

template<typename T, typename S, typename R> 
void foo(T& t, S& s, R& r) 
{ 
    ++t; 
    ++s; 
    ++r; 
} 

int main() 
{ 
    int i = 0; 
    double j = 0; 
    short k = 0; 

    dec_proxy<int> dp1(i); 
    dec_proxy<double> dp2(j); 
    dec_proxy<short> dp3(k); 

    foo(dp1,dp2,dp3); 

    //foo(dec_proxy<int>(i),  <---- Gives an error 
    // dec_proxy<double>(j),  <---- Gives an error 
    // dec_proxy<short>(k));  <---- Gives an error 

    std::cout << "i=" << i << std::endl; 

    return 0; 
} 

El problema es que para los distintos tipos que me gustaría utilizar dec_proxy que actualmente requieren la creación de una instancia especializada de dec_proxy - parece un enfoque muy desordenado y limitado.

Mi pregunta es: ¿Cuál es la forma correcta de pasar tales temporarios efímeros como parámetros de referencia no constantes?

+0

¿Por qué no pasa el objeto por valor? –

+1

Porque así es como se ha definido foo, no puedo cambiar foo y porque foo toma múltiples parámetros. –

+1

En ese caso, excelente pregunta. –

Respuesta

12

Siguiendo el consejo de Stephen, usted debe buscar en la respuesta a How come a non-const reference cannot bind to a temporary object? y simplemente agregar una función miembro que devuelve una referencia dec_proxy, por ejemplo:

dec_proxy &ref() { return *this; }

y llame foo:

foo(
    dec_proxy<int>(i).ref(), 
    dec_proxy<double>(j).ref(), 
    dec_proxy<short>(k).ref()); 

Estoy bastante seguro de que compila.

+0

Buena solución :) – Geoffroy

2

Lo que intenta hacer es pasar un valor r (su new dec_facade<int>(i)) como una referencia lvalue, lo que explica por qué no funciona.

Si compilador apoyarlo, puede utilizar referencias rvalue, utilizando && modificador de tipo: (Apoyo para la referencia rvalue se podría activar mediante el encendido de C++ 0x o C++ 11 apoyo [parcial])

template<typename T> 
void foo(T& t) 
{  
    ++t; 
} 
template<typename T> 
void foo(T&& t) 
{  
    ++t; 
} 

Pero eso solo es una parte del problema. Lo que intenta hacer es aumentar previamente un valor temporal. Eso no tiene sentido, ya que no vivirá después de esa llamada. Su objeto será incrementado, luego destruido.


Otra solución sería eliminar la & de su definición de la función, lo que permitiría que acepte cualquier parámetro. Pero eso tal vez no es lo que quieres.

+0

Esa es de hecho una buena solución para C++ 11, pero esperaba que tal vez también podría haber una manera limpia de hacerlo en C++ 03 y otros. –

+3

No, no es tonto ** **, mira el código de nuevo. La fachada solo delega la llamada, por lo que aunque sea temporal, el efecto no se perderá. –

+0

@KonradRudolph Si el objeto es un valor r, se incrementará pero no verá ningún resultado ya que se destruirá al final del alcance de foo no? – Geoffroy

7

Gracias a MSN, la solución:

no creo que sea correcto mediante la adición de la plantilla de función template<typename T> dec_proxy_impl<T>& dec_proxy(T&t).

Lo que hizo es solo compilador de trampas. Provocará un error de tiempo de ejecución. La función foo requiere la referencia lvaue o lvalue. Pero template<typename T> dec_proxy_impl<T>& dec_proxy(T&t) no devuelve una referencia lvalue válida. En la implementación, crea un objeto temporal y lo devuelve. Después de que finaliza la llamada a la función, se destruirá el objeto temporal. Entonces la referencia de valor pasada a la función foo es incorrecta. En realidad, el objeto al que se hace referencia ya ha sido destruido. El ++t;++s;++r está intentando acceder a los objetos no válidos. El comportamiento no está definido.

La solución de MSN es correcta. El tiempo de vida del objeto dec_proxy<int>(i) es desde su declaración hasta el final de la llamada a la función. Se asegura de que el parámetro en la función foo sea válido.

+0

Tienes razón. Me horroriza que incluso sea posible engañar al compilador de esta forma. Los temporarios deberían tener un tipo separado que no puede, de ninguna manera, ser forzado a un no temporal (esto haría que la solución de MSN también funcione, pero podría soportarlo, especialmente cuando hay referencias de valores). –

+0

@KonradRudolph, los temporales ya pueden tener código arbitrario ejecutándose en ellos; el problema (también mencionado en la respuesta a la que me he vinculado) es si se pueden realizar operaciones implícitas en los temporales o no. Si está manipulando explícitamente temporales llamando a funciones miembro, está bien (porque es explícito). – MSN

+0

@MSN Lo sé, pero subvierte el sistema de tipos. Idealmente, un sistema de tipo prohibiría cualquier operación ilegal. Ahora, es trivial probar que un sistema de ese tipo no puede implementarse en tiempo de compilación (esto resolvería el problema de detención). Pero me gustaría que el sistema de tipos sea lo más estricto posible y me parece (al menos a primera vista) que la fuga del alcance de referencia local se puede controlar estáticamente en todas las circunstancias. –