2010-11-09 9 views
8

Me enfrento con el siguiente problema. Considere la clase siguiente:No permitir tomar puntero/referencia a const a un objeto temporal en C++ (sin C++ 0X)

//Will be similar to bost::reference_wrapper 
template<class T> 
class Ref { 
public: 
    explicit Ref(T& t) : m_ptr(&t) {} 
private: 
    T* m_ptr; 
}; 

y esta función que devuelve un doble

double fun() {return 1.0;} 

Si ahora tenemos

double x = 1.0; 
const double xc = 1.0; 

Ref<double> ref1(x); //OK 
Ref<const double> refc1(cx); //OK 

bien hasta ahora sin embargo,:

//Ref<double> ref2(fun()); //Fails as I want it to 
Ref<const double> refc2(fun()); //Works but I would like it not to 

Es hay una manera de modificar Ref (la forma en que prefiero) pero no la función fun, de modo que la última línea devuelve un error en tiempo de compilación? Tenga en cuenta que puede modificar la firma del constructor (siempre que pueda inicializar la Ref como estaba previsto).

Respuesta

2

Puede que no le guste la sintaxis para usarla, pero haga que el constructor tome un puntero en lugar de una referencia. No puede tomar incluso un puntero de const a un temporal.

Al menos, no sin contrabando a través de otro contenedor que se espera hace que el código incorrecto Obviamente (TM): template <typename T> T *reftoptr(T &t) { return &t; }

Dicho esto, si usted está usando este el camino reference_wrapper se utiliza, los usuarios pueden realmente quiere para capturar los temporales. Siempre que el objeto Ref sea también temporal en la misma expresión completa que el temporal que captura, creo que está bien. Por ejemplo,

some_algorithm(iterator, anotherit, static_cast<Ref<const double> >(fun())); 
+0

Esto es (para mí) el más cercano al problema establecido (con consideraciones también interesantes, especialmente la última), ¡Gracias por tu ayuda! – stepelu

3

No, y su código se puede romper incluso con una referencia normal. Simplemente documente el hecho de que el objeto pasado debe ser persistente.

double *x = new double; 
Ref<double> ref(*x); 
delete x;  
+0

+1. Indique claramente en la documentación cómo se debe usar. Pero no es su responsabilidad dar cuenta del mal uso de su clase. – ereOn

+0

El propósito de esta restricción es ayudar a los usuarios. Dado que es bastante fácil ayudar a los usuarios en este caso, no creo que deba descartarse. Descartarlo detalladamente, si hay alguna razón por la que no ayuda a los usuarios ;-) –

1

Utilice un argumento de puntero para inicializar su miembro de puntero. No use una referencia (const) para esto: use un puntero para inicializar un puntero.

he tenido algunos problemas con las referencias de seguimiento en el pasado, y aunque no está directamente relacionada con su pregunta, es posible encontrar estos dos temas interesantes:

0

Puedes usar una plantilla. U& se deduce a double&, y eso no se vinculará a valores r.

template<class T> 
class Ref { 
public: 
    template<typename U> 
    explicit Ref(U& t, 
       typename boost::enable_if< 
       boost::is_convertible<U, T&> 
       >::type * = 0) 
    : m_ptr(&t) {} 
private: 
    T* m_ptr; 
};