2010-02-18 9 views
5

Ho una funzione simile (. Si prega di non si preoccupano di tornare temporanea con riferimento Questo è solo un esempio per spiegare il problema),Conversione da "foo <T>" a "foo const <const T>" - C++

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

Questo ovviamente non verrà compilato. Sto cercando un modo per garantire che i chiamanti non cambieranno il T di foo. Come posso assicurarlo?

Ho visto il comportamento simile per boost::shared_ptr. shared_ptr<T> è convertibile in const shared_ptr<const T>. Non riuscivo a capire come sta facendo questo.

Qualsiasi aiuto sarebbe fantastico.

+0

Probabilmente stai cercando di assicurarti che i chiamanti non cambino il * f * di foo. –

risposta

0

Se non mi sbaglio, l'attuazione boost::shared_ptr ha un costruttore non esplicita che prende un riferimento const T& come argomento e quindi utilizza un const_cast sul puntatore del RHS per rimuovere il const, permettendo conversioni implicite tra di loro.

Qualcosa di simile a questo:

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

è questo che stai cercando?

+0

'const_cast' == Red aringa per me :) L'OP sta cercando qualcosa che faccia il contrario (che è molto più naturale). –

1

Supponendo che Foo è definito qualcosa di simile:

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; 
}; 

Poi, al fine di garantire che i clienti di Foo non modificano m_value (suppongo che questo è ciò che si intende per "Cerco un modo per garantire i chiamanti non cambierà la T di foo "), è necessario const-qualificare l'oggetto foo piuttosto che la sua parametro di template, cioè

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

Pertanto, la funzione get_foo dovrebbe restituire un const Foo<T>&, non un const Foo<const T>&.

Ecco un completo, ad esempio compilabile:

#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

Sebbene questa sia una buona risposta, ci sono casi in cui 'const foo ' potrebbe essere davvero ciò che è necessario, in particolare se 'T' è un tipo di puntatore o se foo memorizza un' T * '. –

+0

@Tyler D'accordo, l'OP era poco chiaro - mentre chiede come convertire Foo in Foo , si afferma anche "Sto cercando un modo per garantire che i chiamanti non cambino il T di foo", che è ciò che la mia risposta mira a raggiungere per mezzo della qualificazione costante dell'oggetto Foo stesso. –

+0

Grazie per la risposta. Sto cercando ciò che @Tyler ha menzionato. –

9

Il compilatore vede foo<T> e foo<const T> come due tipi completamente diversi e non collegati, quindi la classe foo deve supportare esplicitamente proprio come con qualsiasi altra conversione. Se si ha il controllo sulla classe foo, è necessario fornire un costruttore di copia o un operatore di conversione implicita (o entrambi).

+0

Grande. Grazie mille. –

0

Prima di tutto, si restituisce un oggetto locale per riferimento ... non va bene.

foo e foo sono due tipi diversi, quindi è necessario scrivere codice (costruttori di conversione) per convertirli esplicitamente.

per ottenere ciò che voleva, considerare questo:

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 

Se hai fatto il tuo incapsulamento correttamente e solo i membri di accesso con getter const e setter non const, questo dovrebbe essere abbastanza buono per voi. Ricorda se le persone vogliono veramente cambiare il tuo oggetto, possono sempre const_cast. Const-correctness è solo per cogliere errori non intenzionali.