2010-02-18 9 views
5

Ich habe eine Funktion wie (. Sie kümmern sich nicht um durch Bezugnahme temporäre Rückkehr Dies ist nur ein Beispiel, das Problem zu erklären),von „foo <T>“ Konvertieren in „const foo <const T>“ - C++

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

Dies wird natürlich nicht kompilieren. Ich bin auf der Suche nach einer Möglichkeit, sicherzustellen, dass Anrufer die T von foo nicht ändern. Wie kann ich das sicherstellen?

Ich habe das ähnliche Verhalten für boost::shared_ptr gesehen. shared_ptr<T> ist umwandelbar in const shared_ptr<const T>. Ich konnte nicht herausfinden, wie es das macht.

Jede Hilfe wäre großartig.

+0

Sie versuchen wahrscheinlich sicherzustellen, dass Anrufer das * f * von foo nicht ändern. –

Antwort

0

Wenn mich nicht alles täuscht, die boost::shared_ptr Implementierung hat einen nicht explizit Konstruktor, der eine const T& Referenz als Argument und verwendet dann einen const_cast auf der Zeiger RHS die const, so dass implizite Konvertierungen zwischen ihnen zu entfernen.

Etwas wie folgt aus:

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

Ist das, was Sie suchen?

+0

'const_cast' == Red Hering für mich :) Das OP ist auf der Suche nach etwas, das das Gegenteil tut (was viel natürlicher ist). –

1

dass Foo Unter der Annahme, etwas wie folgt definiert:

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

, um dann, um sicherzustellen, dass Kunden von Foo nicht ändern m_value (Ich gehe davon aus, dass das ist, was durch „gemeint ist, ich suche ein Art und Weise Anrufer um sicherzustellen, dass die T von foo ") nicht ändern, müssen Sie statt dessen Template-Parameter die foo Objekt const qualifizieren, dh

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

Deshalb sollte Ihre get_foo Funktion ein const Foo<T>& zurückzukehren, kein .

Hier ist ein komplettes, übersetzbar Beispiel:

#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

Während dies eine gute Antwort ist, gibt es Fälle, in denen "const foo " wirklich das ist, was benötigt wird, insbesondere wenn "T" ein Zeigertyp ist oder wenn foo ein "T *" speichert. –

+0

@Tyler Einverstanden, das OP war etwas unklar - während es fragt, wie Foo in Foo konvertieren, es heißt auch "Ich bin auf der Suche nach einem Weg, um sicherzustellen, Anrufer werden nicht die T von foo ändern", was ist Meine Antwort zielt darauf ab, das Foo-Objekt selbst zu qualifizieren. –

+0

Danke für die Antwort. Ich bin auf der Suche nach @Tyler. –

9

Der Compiler sieht foo<T> und foo<const T> als zwei völlig unterschiedliche und nicht verwandte Arten, so dass die foo Klasse dies explizit wie bei jeder anderen Konvertierung unterstützen muß. Wenn Sie die Klasse foo steuern, müssen Sie einen Kopierkonstruktor oder einen impliziten Konvertierungsoperator (oder beides) angeben.

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

Großartig. Vielen Dank. –

0

Zuerst geben Sie ein lokales Objekt per Referenz zurück ... das ist nicht gut.

foo und foo sind zwei verschiedene Typen, daher müssen Sie Code (Konvertierungskonstruktoren) schreiben, um sie explizit zu konvertieren.

zu bekommen, was Sie wollen, bedenken Sie:

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 

Wenn Sie Ihre Kapselung richtig gemacht und nur den Zugriff Mitglieder mit konst Getter und nicht konstanten Setter, sollte dies für Sie gut genug sein. Denken Sie daran, wenn Menschen Ihr Objekt wirklich ändern wollen, können sie immer const_cast. Const-Korrektheit soll nur unbeabsichtigte Fehler erfassen.