2010-02-18 21 views
5

Mam funkcji takich jak (. Proszę nie dbają o powrocie tymczasowy przez odniesienie Jest to tylko przykład, aby wyjaśnić problem),Konwersja z "foo <T>" do "const foo <const T>" - C++

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

To oczywiście nie da się skompilować. Szukam sposobu, aby upewnić się, że rozmówcy nie zmienią T z foo. Jak mogę to zapewnić?

Widziałem podobne zachowanie dla boost::shared_ptr. shared_ptr<T> jest zamieniany na const shared_ptr<const T>. Nie mogłem zrozumieć, jak to robi.

Każda pomoc będzie świetna.

+0

Prawdopodobnie próbujesz upewnić się, że rozmówcy nie zmienią * f * foo. –

Odpowiedz

0

Jeśli się nie mylę, realizacja boost::shared_ptr ma zakaz wyraźny konstruktora, że ​​trwa const T& odniesienie jako argument, a następnie wykorzystuje const_cast na wskaźnik RHS do usunięcia const, pozwalając niejawne konwersje między nimi.

coś takiego:

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

Czy tego szukasz?

+0

'const_cast' == Czerwony śledzia dla mnie :) OP szuka czegoś, co robi coś wręcz przeciwnego (co jest dużo bardziej naturalne). –

1

Zakładając, że Foo jest zdefiniowane tak:

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

Następnie, w celu zapewnienia, że ​​klienci Foo nie zmieniają m_value (zakładam, że to, co należy rozumieć przez „ja szukam sposób, aby zapewnić rozmówców nie zmieni t foo "), trzeba const zakwalifikować obiekt foo raczej niż jego parametr szablonu, czyli

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

Dlatego czynność get_foo powinien zwrócić const Foo<T>&, a nie const Foo<const T>&.

Oto kompletny, compilable przykład:

#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

Chociaż jest to świetna odpowiedź, zdarzają się przypadki, gdy 'const foo ' może rzeczywiście być tym, co jest potrzebne, szczególnie jeśli 'T' jest typem wskaźnika lub jeśli foo przechowuje' T * '. –

+0

@Tyler Uzgodnione, OP był nieco niejasne - podczas gdy pyta, jak przekonwertować Foo na Foo , to również stwierdza "Szukam sposobu, aby zapewnić, że rozmówcy nie zmienią T of foo", co jest tym, co moja odpowiedź ma na celu osiągnięcie przez const-qualifying samego obiektu Foo. –

+0

Dzięki za odpowiedź. Szukam tego, o czym wspomniał @Tyler. –

9

kompilator widzi foo<T> i foo<const T> jako dwóch zupełnie różnych i niespokrewnionych typów, więc klasa foo musi obsługiwać ten wyraźnie tak jak w przypadku każdego innego nawrócenia. Jeśli masz kontrolę nad klasą foo, musisz dostarczyć konstruktora kopiowania lub niejawnego operatora konwersji (lub oba typy).

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

Świetnie. Dziękuję bardzo. –

0

Po pierwsze, zwracasz lokalny obiekt przez odniesienie ... to nie jest dobre.

foo i foo są dwoma różnymi typami, więc będziesz musiał napisać kod (konstruktory konwersji), aby jawnie je skonwertować.

Aby uzyskać to, czego chciał, rozważ to:

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 

Jeśli zrobić swoje enkapsulacji poprawnie i tylko członkowie dostępu z getter const i non-const ustawiaczy, to powinno być wystarczająco dobre dla ciebie. Pamiętaj, że jeśli ludzie naprawdę chcą zmienić obiekt, zawsze mogą const_cast. Const-correctness służy tylko do złapania niezamierzonych błędów.

Powiązane problemy