Problem mit const-correctness und std::map::find
-
Hallo!
Habe ein Problem mit der const-correctness zweier meiner Klassen, ich vermute, dass es von der Art und Weise herrührt, wie ich std:
:find benutze. Dort gibt es jedenfalls immer einen Fehler beim Kompilieren, den ich mir jedoch nicht erklären kann.Hier mal ein (hoffentlich) aussagekräftiger Codeschnipsel:
/// Count how many pairs are waiting for processing. ULong_t Sni3AnalysisStorage::getNoUnprocessedPairs(const SN_Analysis_t* pcaller) const { if ( mColumnBufferDeque.empty() ) { return 0; } /** * I use a map with pointers as key values and iterators as mapped values. */ std::map< SN_Analysis_t*, std::deque<Sni3ColumnDataPair>::iterator >::const_iterator it; /** * Below, on the next line I always get a cryptic compiler error, but only as long as "pcaller" is const: */ it = mBufferAnalysisMap.find( pcaller ); /// this is strange, because std::map::find has two signatures: /// iterator find ( const key_type& x ); /// const_iterator find ( const key_type& x ) const; if ( it != mBufferAnalysisMap.end() ) { std::deque<Sni3ColumnDataPair>::const_iterator dit = it->second; return std::distance( dit, mColumnBufferDeque.end() ); } else { return 0; } } /// Sni3SicoAnalysis is derived from SN_Analysis_t. And because ceckBufferLength() is const, the this-pointer inside is const, too. /// Therefore I need the argument of getNoUnprocessedPairs to be const... void Sni3SicoAnalysis::checkBufferLength()const { ULong_t noPairs = mpAnalysisStorage->getNoUnprocessedPairs(this); if ( noPairs == mSicoDelta ) { /// Everything is fine. } else { /// Find out, what went wrong and throw an exception! ///.... } }Kann mir jemand einen Rat geben, wie ich an dieser Stelle weiterkommen könnte, ohne auf const-correctness zu verzichten und ohne die pointer in meiner map auch const zu setzen?
-
1. Wieso gebt ihr uns nie die Fehlermeldungen? Für uns sind die nicht kryptisch, wir können die lesen und sie helfen uns.
Statt
SN_Analysis_t*als Key in der Map, nimmst duSN_Analysis_t const*. Eine andere "sinnvolle" Lösung gibt es nicht. Denn:int a; int const* cp = &a; int* p = cp; // Fehler! Wieso, sollte klar sein.3. Das hat auch nichts mit den Funktionssignaturen von
findzu tun.4. const-correctness heisst übrigens nicht einfach überall
constzu verwenden, sondern dort wo es nötig ist
Grüssli
-
Dravere schrieb:
1. Wieso gebt ihr uns nie die Fehlermeldungen? Für uns sind die nicht kryptisch, wir können die lesen und sie helfen uns.

Const-Correctness wird oft für get-Methoden benutzt, bzw. für Methoden, in denen nichts verändert wird.
#include <iostream> class Test { private: int testvariable; public: Test() : testvariable(5) {} int testmethode() const { return testvariable; } }; int main() { Test t; t.testmethode(); }Versuchst du jetzt sowas hier
int testmethode() const { testvariable = 5; return testvariable; }Solltest du eine Fehlermeldung dieser Art bekommen:
error: assignment of data-member `Test::testvariable' in read-only structure
Und da steht es "read-only".
-
Wo man const-Methoden verwendet ist mir durchaus klar gewesen, ich habe ja auch meine eigene Methode genau deshalb const gemacht, weil sie nur überprüfen muss, ob ein key in der map enthalten ist, was ja noch keine Werte ändern dürfte... Mein Punkt ist vielmehr der folgende:
class A { public: bool hasKey(const int* key)const; // guter Kandidat für const-Methode... private: std::map<int*,string> mMap; }; // Das klappt nicht... bool A::hasKey(const int* key)const { std::map<int*,string>::const_iterator iter; iter = mMap.find(key); // ERROR (das hier ist Zeile 32) if ( iter == mMap.end() ) return true; else return false; } class B { public: bool hasKey(const int key)const; private: std::map<int,string> mMap; }; // Hier klappt es tadellos... bool B::hasKey(const int key)const { std::map<int,string>::const_iterator iter; iter = mMap.find(key); // OK if ( iter == mMap.end() ) return true; else return false; }Das erste Beispiel liefert:
**** Build of configuration Linux GCC for project sni3daq ****
make all
make all-recursive
make[1]: Betrete Verzeichnis '/home/goesta/sni3daq'
Making all in src
make[2]: Betrete Verzeichnis '/home/goesta/sni3daq/src'
Making all in common
make[3]: Betrete Verzeichnis '/home/goesta/sni3daq/src/common'
/bin/bash ../../libtool --tag=CXX --mode=compile g++ -DHAVE_CONFIG_H -I. -I../.. -I/usr/bin/root.5.20/include/root -g -O2 -pthread -m32 -I/usr/bin/root.5.20/include/root -I/usr/include/libxml2 -I/usr/include -MT Sni3AnalysisStorage.lo -MD -MP -MF .deps/Sni3AnalysisStorage.Tpo -c -o Sni3AnalysisStorage.lo Sni3AnalysisStorage.C
libtool: compile: g++ -DHAVE_CONFIG_H -I. -I../.. -I/usr/bin/root.5.20/include/root -g -O2 -pthread -m32 -I/usr/bin/root.5.20/include/root -I/usr/include/libxml2 -I/usr/include -MT Sni3AnalysisStorage.lo -MD -MP -MF .deps/Sni3AnalysisStorage.Tpo -c Sni3AnalysisStorage.C -fPIC -DPIC -o .libs/Sni3AnalysisStorage.o
Sni3AnalysisStorage.C: In member function »bool A::hasKey(const int*) const«:
Sni3AnalysisStorage.C:32: Fehler: Die Übergabe von »const std::map<int*, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<int*>, std::allocator<std::pair<int* const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >« als »this«-Argument von »typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const _Key&) [with _Key = int*, _Tp = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, _Compare = std::less<int*>, _Alloc = std::allocator<std::pair<int* const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >]« streicht Qualifizierer
Sni3AnalysisStorage.C:32: Fehler: ungültige Umwandlung von »const int*« in »int*«
Sni3AnalysisStorage.C:32: Fehler: Argument 1 von »typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator std::map<_Key, _Tp, _Compare, _Alloc>::find(const _Key&) [with _Key = int*, _Tp = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, _Compare = std::less<int*>, _Alloc = std::allocator<std::pair<int* const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >]« wird initialisiert
make[3]: Verlasse Verzeichnis '/home/goesta/sni3daq/src/common'
make[3]: *** [Sni3AnalysisStorage.lo] Fehler 1
make[2]: *** [all-recursive] Fehler 1
make[2]: Verlasse Verzeichnis '/home/goesta/sni3daq/src'
make[1]: *** [all-recursive] Fehler 1
make[1]: Verlasse Verzeichnis '/home/goesta/sni3daq'
make: *** [all] Fehler 2Und das zweite Beispiel kompiliert problemlos. Das ist genau was ich nicht verstehe.
-
Das problem ist eigentlich relativ simpel und darauf zurück zuführen das man sich das die schreibweise mit dem const anders angewöhnen sollte, weil einem der Fehler dann auch auffallen mag.
Deine Signatur der Method:
bool hasKey(const int* key)const;bessere Schreibweise:
bool hasKey(int const * key)const;'int const * key' Liest sich von Links nach Rechts:
Key ist ein Zeiger auf ein konstantes Objekt vom Typ int.Deine map speichert int * als Key ab (sprich Zeiger auf ein Objekt vom Typ int)
Key = int *
und Key const & entspricht dann int * const &
und nicht int const * const &Würdest du die Signatur auf:
bool hasKey(int * const key)const;ändern
dann funktioniert das ganze auch.Ich hoffe ich hab genug Verwirrung gestiftet.