const/non-const Funktionspärchen
-
Hallo,
ich kann mir bei const/non-const Funktionspärchen nie merken, welcher cast gültig ist. Ich möchte zwei von der Funktionalität identische Funktionen durch eine Funktion implementieren, eine const- und eine non-const-Variante. Da gibt's jetzt zwei Möglichkeiten:
-
Implementierung in der const-Funktion
In der non-const Funktion wird nach const gecastet, die const-Funktion aufgerufen und das Ergebnis nach non-const gecastet. -
Implementierung in der non-const-Funktion
In der const Funktion wird nach non-const gecastet, die non-const-Funktion aufgerufen und das Ergebnis nach const gecastet.
Nach meinem Verständnis sollte Variante 1 die richtige sein, ich darf ein non-const Objekt nach const casten. Weil ich an dieser Stelle weiß, dass das Objekt non-const ist, darf ich es auch wieder von const nach non-const zurückcasten.
Variante 2 sollte falsch sein, da die non-const Funktion das Objekt verändern könnte, und das darf bei einem const Objekt nicht passieren. Also ist der cast von const nach non-const nicht erlaubt. Oder darf ich das hier brechen, weil ich weiß, dass auch die non-const Variante das Objekt nicht verändert?
Im konkreten Beispiel funktioniert das für iteratoren nicht, ein
iteratorkann zwar in einenconst_iteratorkonvertiert werden, aber nicht umgekehrt. Für die Zeigervariante funktioniert das.Was ist also die korrekte Lösung für iteratoren?
#include <vector> #include <algorithm> #include <iostream> using namespace std; struct Test { std::vector<int> v; using iterator = std::vector<int>::iterator; using const_iterator = std::vector<int>::const_iterator; iterator find( int val ) { return std::find( v.begin(), v.end(), val ); } const_iterator find( int val ) const { return const_cast<Test*>( this )->find( val ); } int* find2( int val ) { return const_cast<int*>( const_cast<Test const*>( this )->find2( val )); } int const* find2( int val ) const { auto pos = std::find( v.begin(), v.end(), val ); return pos == v.end() ? nullptr : &(*pos); } }; int main() { Test c; Test const n; c.find( 13 ); n.find( 13 ); c.find2( 13 ); n.find2( 13 ); return 0; }Falls sich jemand fragen sollte:
Ich habe die Variablennamencundnvertauscht,csollte eigentlich const sein undnnon-const.
-
-
@DocShoe
Eine Frage: Warum nutzt du nichtauto? Das wurde doch gerade dazu erfunden, um das Iteratoren-Handling zu vereinfachen:#include <vector> #include <algorithm> #include <iostream> using namespace std; struct Test { std::vector<int> v; /*using iterator = std::vector<int>::iterator; using const_iterator = std::vector<int>::const_iterator;*/ auto find(int val) { return std::find(v.begin(), v.end(), val); } auto find(int val) const { return std::find(v.begin(), v.end(), val); } auto* find2(int val) { auto pos = std::find(v.begin(), v.end(), val); return pos == v.end() ? nullptr : &(*pos); } auto* find2(int val) const { auto pos = std::find(v.begin(), v.end(), val); return pos == v.end() ? nullptr : &(*pos); } }; int main() { Test c; Test const n; c.find(13); n.find(13); *c.find2(13) = 14; //*n.find2(13) = 14; return 0; }Sorry, habe noch die Funktion
find2nachgezogen.
-
Also angenommen, die
findFunktion ist jetzt nicht ganz so trivial, dann möchte ich den Code für die const/non-const Varianten nicht duplizieren, selbst wenn das nur fünf Zeilen sind.
-
Die Const Correctness ist hier eigentlich sehr cool. Weil abhängig von const bei den find Funktionen auch die entsprechenden const std::find Funktionen aufgerufen wird. Entsprechend dessen liefert automatisch auch const Iteratoren zurück.
Wenn du Visual Studio nutzt, schaue dir mal die Definitionen im IntelliSense an.
Edit: Hab deinen neuen Beitrag zu spät gelesen.
-
Also, ich bin leider kein Freund von
const_cast, da ich schon so manche Embedded C++ Compiler gesehen habe, welcheconstauf ihre ganz eigene Weise interpretiert haben.Deswegen habe ich mal ein wenig gegrübelt. Du sagst dass die
findFunktion nicht trivial ist, also warum nicht eine eigene Template-Find Funktion schreiben`?#include <vector> #include <algorithm> #include <iostream> using namespace std; template <class _InIt, class _Ty> _InIt MySpecialFind(_InIt _First, const _InIt _Last, const _Ty& _Val) { // Fill me return std::find(_First, _Last, _Val); } struct Test { std::vector<int> v; auto find(int val) { return MySpecialFind(v.begin(), v.end(), val); } auto find(int val) const { return MySpecialFind(v.begin(), v.end(), val); } }; int main() { Test c; Test const n; c.find(13); n.find(13); //*c.find2(13) = 14; //*n.find2(13) = 14; return 0; }
-
Ja, auf sowas wird's wohl irgendwie hinauslaufen. Hatte nur gehofft, dass man das analog zu find2 machen könnte, aber die beiden iterator-Typen sind halt unterschiedlich und eben nicht der gleiche Typ mit anderen Qualifizierern.
Danke für deinen Vorschlag.