2010-02-18 22 views
5

tengo una función como (. Por favor, no se preocupan por regresar temporal por referencia Este es sólo un ejemplo para explicar el problema),La conversión de "foo <T>" a "foo const <const T>" - C++

const foo<const int>& get_const() 
{ 
    foo<int> f; 
    return f; 
} 

Obviamente, esto no se compilará. Estoy buscando una manera de asegurar que las personas que llaman no cambien el T de foo. ¿Cómo puedo asegurar eso?

He visto el comportamiento similar para boost::shared_ptr. shared_ptr<T> es convertible a const shared_ptr<const T>. No pude entender cómo está haciendo esto.

Cualquier ayuda sería genial.

+0

Probablemente esté intentando asegurarse de que las personas que llaman no cambien el * f * de foo. –

Respuesta

0

Si no me equivoco, la implementación boost::shared_ptr tiene un constructor no explícita que toma una referencia const T& como un argumento y luego utiliza un const_cast el puntero del lado derecho para eliminar el const, lo que permite conversiones implícitas entre ellos.

Algo como esto:

shared_ptr(const shared_ptr<const T>& r) : ptr(const_cast<T*>(r.ptr)) {} 

Es eso lo que está buscando?

+0

'const_cast' == Red arenque para mí :) El OP está buscando algo que haga lo contrario, de hecho (que es mucho más natural). –

1

Suponiendo que Foo se define así:

template<typename T> class Foo 
{ 
public: 
    Foo(const T& value) : m_value(value) { } 
    const T& getValue() const { return m_value; } 
    void setValue(const T& value) { m_value = value; } 
private: 
    T m_value; 
}; 

Luego, con el fin de garantizar que los clientes de Foo no modifican m_Value (supongo que esto es lo que se entiende por "Busco a una manera de asegurar que llaman no va a cambiar la camiseta del foo "), es necesario cONST-calificar el objeto de foo en lugar de su parámetro de plantilla, es decir

Foo<int> x(1); 
x.setValue(2); // OK 
const Foo<int> y(1); 
y.setValue(2); // does not compile 

Por lo tanto, su función debe devolver un get_foo const Foo<T>&, no una const Foo<const T>&.

Aquí es una solución completa, compilables ejemplo:

#include <iostream> 

template<typename T> class Foo 
{ 
public: 
    Foo(const T& value) : m_value(value) { } 
    const T& getValue() const { return m_value; } 
    void setValue(const T& value) { m_value = value; } 
private: 
    T m_value; 
}; 

template<class T> class Owner 
{ 
public: 
    Owner(const T& value) : m_foo(value) { } 
    Foo<T>& getFoo() { return m_foo; } 
    const Foo<T>& getConstFoo() const { return m_foo; } 

private: 
    Foo<T> m_foo; 
}; 


int main(int argc, char** argv) 
{ 
    Owner<int> x(1); 
    x.getFoo().setValue(2); 
    // x.getConstFoo().setValue(3); // will not compile 
} 
+0

Si bien esta es una gran respuesta, hay casos en que 'const foo ' podría ser realmente lo que se necesita, particularmente si 'T' es un tipo de puntero o si foo almacena' T * '. –

+0

@Tyler De acuerdo, el OP no estaba nada claro, aunque se pregunta cómo convertir Foo en Foo , también dice "Estoy buscando una forma de garantizar que los llamantes no cambien la T de foo", que es lo que mi respuesta apunta a lograr calificando constmcionalmente el objeto Foo. –

+0

Gracias por la respuesta. Estoy buscando lo que @Tyler mencionó. –

9

El compilador ve foo<T> y foo<const T> como dos tipos completamente diferentes y no relacionadas, por lo que la clase foo necesita apoyar explícitamente al igual que con cualquier otra conversión. Si tiene control sobre la clase foo, debe proporcionar un constructor de copia o un operador de conversión implícita (o ambos).

template<typename T> 
class foo 
{ 
public: 

    // Regular constructor 
    foo(T t) : t(t) {} 

    // Copy constructor (works for any type S convertable to T, in particular S = non-const T if T is const) 
    // Remember that foo<T> and foo<S> are unrelated, so the accessor method must be used here 
    template<typename S> foo (const foo<S>& copy) : t(copy.getT()) {} 

    // Accessor 
    T getT() const { return t; } 

    // Conversion operator 
    operator foo<const T>() const { return foo<const T>(t); } 

private: 

    T t; 
}; 
+0

Genial. Muchas gracias. –

0

En primer lugar, está devolviendo un objeto local por referencia ... eso no es bueno.

foo y foo son dos tipos diferentes, por lo que tendrá que escribir el código (constructores de conversión) para convertirlos explícitamente.

para conseguir lo que quería, considere esto:

template <typename T> 
struct foo {T* t;}; 

const foo<int>& get_const(const foo<int>& f) { 
    return f; 
} 

foo<int> f; 
const foo<int>& cf = get_const(f); 
f.t = 0; // ok, f is not const 
*cf.t = 0; // ok because cf.t is const but what cf.t points to is not 
cf.t = 0; // compiler error cf.t is const and cannot be lvalue 

foo<int>& cf = get_const(f); // compiler error, cannot convert non-const to const without const_cast 

Si hecho su encapsulación correctamente y sólo los miembros de acceso con getter y setters const no constante, esto debe ser lo suficientemente bueno para usted. Recuerde que si las personas realmente desean cambiar su objeto, siempre pueden const_cast. Const-corrección es solo para detectar errores involuntarios.

Cuestiones relacionadas