BCB6 const correctness Problem
-
Hi,
weil mich an der std::deque ein paar Dinge stören (die Granularität der einzelnen Knoten ist mit 8 Elementen einfach zu klein, bei kleinen Datenstrukturen wird der Speicheroverhead einfach zu gross, alsdass ich vernünftig damit arbeiten könnte) versuche ich mich an der Implementation einer eigenen deque-ähnlichen Klasse.
Ich poste mal den minimalen Code, der den Fehler reproduziert, er tritt nur beim BCB6 auf, unter Codegear 2007 kompiliert und läuft er fehlerfrei. Der gepostete Code hat Speicherlecks, aber dafür ist es auch wirklich der mininmal Code.#pragma hdrstop #include <vector> #include <stdexcept> #include <iostream> template<typename T, unsigned int B> class Deque; template<typename T, unsigned int B> struct const_traits { typedef const Deque<T,B> deque_type; typedef const T value_type; }; template<typename T, unsigned int B> struct nonconst_traits { typedef Deque<T,B> deque_type; typedef T value_type; }; template<typename deque_traits> class DequeIterator { typedef deque_traits::deque_type deque_type; typedef deque_traits::value_type value_type; unsigned int Position; deque_type* Owner; public: DequeIterator( deque_type* owner, unsigned int pos ) : Owner( owner ), Position( pos ) { } value_type& operator*() { return Owner->operator[]( Position ); } DequeIterator& operator++() { Position++; return *this; } bool operator!=( const DequeIterator& op ) const { return Position != op.Position; } }; template<typename Type, unsigned int BlockSize> class Deque { private: // Typdefinition für interne Vektoren typedef std::vector<Type> DataVector_t; typedef DataVector_t* DataVectorPtr_t; typedef std::vector<DataVectorPtr_t> ContainerType; public: enum { block_size = BlockSize }; typedef Type value_type; typedef value_type& reference; typedef const value_type& const_reference; typedef Deque<Type,BlockSize> deque_type; // Typdefinitionen für Iteratoren typedef DequeIterator<nonconst_traits<Type,BlockSize> > iterator; typedef DequeIterator<const_traits<Type,BlockSize> > const_iterator; private: // Deque aus shared_ptr auf Vektoren fester Grösse ContainerType Data; public: Deque() { // Platz für 1000 Elemente reservieren Data.reserve( 1000 ); } void push_back( const_reference Value ) { // Liste leer oder letzter Block voll? if( true == empty() || Data.back()->size() == BlockSize ) { // neuen Block anlegen und hinten in deque einfügen Data.push_back( new DataVector_t() ); Data.back()->reserve( BlockSize ); } // Datum in letzten Block einfügen Data.back()->push_back( Value ); } bool empty() const { // Deque leer? Da keine leeren Blöcke existieren kann gegen empty() // geprüft werden return Data.empty(); } unsigned int size() const { // Vektor leer? if( true == empty() ) { // ja, dann existiert kein Element return 0; } // alle Blöcke bis auf den letzten sind voll, der letzte kann nur // teilweise gefüllt sein return (Data.size() -1) * BlockSize + Data.back()->size(); } reference operator[]( unsigned int uiIndex ) { // Block Nr. und Offset bestimmen unsigned int uiBlockID = uiIndex / BlockSize; unsigned int uiOffset = uiIndex % BlockSize; // Element zurückgeben return (*Data[uiBlockID])[uiOffset]; } const_reference operator[]( unsigned int uiIndex ) const { // Block Nr. und Offset bestimmen unsigned int uiBlockID = uiIndex / BlockSize; unsigned int uiOffset = uiIndex % BlockSize; // Element zurückgeben return (*Data[uiBlockID])[uiOffset]; } iterator begin() { return iterator( this, 0 ); } iterator end() { return iterator( this, size() ); } const_iterator begin() const { return const_iterator( this , 0 ); // Zeile 154 } const_iterator end() const { return const_iterator( this, size() ); // Zeile 159 } }; void const_index_test( const Deque<int,5>& d ) { std::cout << "const index test:" << std::endl; for( unsigned int uiIndex = 0; uiIndex < d.size(); ++uiIndex ) { std::cout << d[uiIndex] << std::endl; } } void nonconst_index_test( Deque<int,5>& d ) { std::cout << "nonconst index test:" << std::endl; for( unsigned int uiIndex = 0; uiIndex < d.size(); ++uiIndex ) { std::cout << d[uiIndex] << std::endl; } } void const_test( const Deque<int,5>& d ) { Deque<int,5>::const_iterator b = d.begin(); Deque<int,5>::const_iterator e = d.end(); std::cout << "const test:" << std::endl; while( b != e ) { std::cout << (*b) << std::endl; ++b; } } void nonconst_test( Deque<int,5>& d ) { Deque<int,5>::iterator b = d.begin(); Deque<int,5>::iterator e = d.end(); std::cout << "nonconst test:" << std::endl; while( b != e ) { std::cout << *b << std::endl; *b = 0; ++b; } } #pragma argsused int main(int argc, char* argv[]) { Deque<int,5> d; for( int i = 0; i < 17; ++i ) { d.push_back( i ); } const_index_test( d ); nonconst_index_test( d ); const_test( d ); nonconst_test( d ); nonconst_test( d ); return 0; }
`
[C++ Error] main.cpp(154): E2034 Konvertierung von 'const Deque<int,5> * const' nach 'Deque<int,5> *' nicht möglich
Vollständiger Parser-Kontext
main.cpp(153): Entscheidung zum Instanziieren: DequeIterator<const_traits<int,5> > Deque<int,5>::begin() const
--- Rücksetzen des Parser-Kontexts für die Instanziierung...
main.cpp(59): class Deque<Type,BlockSize>
main.cpp(153): Analyse von: DequeIterator<const_traits<int,5> > Deque<int,5>::begin() const
[C++ Error] main.cpp(154): E2342 Keine Übereinstimmung des Typs beim Parameter 'owner' ('Deque<int,5> *' erwartet, 'const Deque<int,5> *' erhalten)
Vollständiger Parser-Kontext
main.cpp(153): Entscheidung zum Instanziieren: DequeIterator<const_traits<int,5> > Deque<int,5>::begin() const
--- Rücksetzen des Parser-Kontexts für die Instanziierung...
main.cpp(59): class Deque<Type,BlockSize>
main.cpp(153): Analyse von: DequeIterator<const_traits<int,5> > Deque<int,5>::begin() const
[C++ Error] main.cpp(159): E2034 Konvertierung von 'const Deque<int,5> * const' nach 'Deque<int,5> *' nicht möglich
Vollständiger Parser-Kontext
main.cpp(158): Entscheidung zum Instanziieren: DequeIterator<const_traits<int,5> > Deque<int,5>::end() const
--- Rücksetzen des Parser-Kontexts für die Instanziierung...
main.cpp(59): class Deque<Type,BlockSize>
main.cpp(158): Analyse von: DequeIterator<const_traits<int,5> > Deque<int,5>::end() const
[C++ Error] main.cpp(159): E2342 Keine Übereinstimmung des Typs beim Parameter 'owner' ('Deque<int,5> *' erwartet, 'const Deque<int,5> *' erhalten)
Vollständiger Parser-Kontext
main.cpp(158): Entscheidung zum Instanziieren: DequeIterator<const_traits<int,5> > Deque<int,5>::end() const
--- Rücksetzen des Parser-Kontexts für die Instanziierung...
main.cpp(59): class Deque<Type,BlockSize>
main.cpp(158): Analyse von: DequeIterator<const_traits<int,5> > Deque<int,5>::end() const
`
Die Fehlermeldungen werden für die Zeilen 154 und 159 auf, weil der Compiler die const correctness nicht auf die Reihe kriegt. Für Vorschläge oder Hinweise zur Lösung wäre ich echt dankbar.
-
Offenbar hat der BCC bis zur Version 5.8 (C++Builder 2006) hier einen Bug.
DocShoe schrieb:
Für Vorschläge oder Hinweise zur Lösung wäre ich echt dankbar.
Warum benutzt du nicht den C++Builder 2007?
Falls du den älteren C++Builder unbedingt benutzen mußt, kannst du einen klassischen Compilerbug-Workaround verwenden:
#if defined(__BORLANDC__) && (__BORLANDC__ < 0x590) #define _BCC_WORKAROUND_CONST_CAST(type,expr) const_cast <type> (expr) #else #define _BCC_WORKAROUND_CONST_CAST(type,expr) expr #endif ... const_iterator begin() const { return const_iterator(_BCC_WORKAROUND_CONST_CAST (Deque*, this), 0); } const_iterator end() const { return const_iterator(_BCC_WORKAROUND_CONST_CAST (Deque*, this), size ()); }
-
Hallo,
hab das Problem gerade anderweitig (und extrem unschön gelöst): Statt eines typedefs benutze ich nun zwei verschiedene Iterator Klassen (jedenfalls solange ich mit dem BCB6 arbeiten muss). Wenn wir dann auf den 2007 umgestiegen sind kann ich die Sonderbehandlung für den const_iterator wieder entfernen.
Wir arbeiten noch mit dem BCB6, weil wir einige recht grosse Projekte haben, die auch auf Third Party Bilbiotheken zurückgreifen, die den Builder 2007 nicht unterstützen (z.B. weil die Bibliothek nicht mehr supported wird). Dann muss also Ersatz gefunden oder der Quelltext soweit angepasst werden, dass er auch unter dem 2007 kompiliert und korrekt funktioniert.
-
Bravo?