const wegcasten funtioniert nicht
-
const_cast
unddynamic_cast
sehen schon mal verdächtig aus. Warum nimmt die Funktion nicht einenT*
? Und kannst du dendynamic_cast
nicht mit virtuellen Funktionen umgehen?Vielleicht grundsätzlich: Was genau willst du erreichen?
-
const wegcasten funktioniert, wenn vorher const hingecastet wurde.
mach mal ein möglichst kleines aber vollständiges programm mit dem fehler und poste den code. dann können wir das in den compiler stopfen und den fehler wegfummeln. so freistehend fehlt einfach zu viel, es könnte an soo vielen anderen sachen liegen…
-
_counter wird wohl eher das Problem sein, wenn es ein Member ist, ist es in einer const-Funktion const!
-
volkard schrieb:
const wegcasten funktioniert, wenn vorher const hingecastet wurde.
Was sowas hier heisst?
void doShit(SomeClass const * x) { SomeClass * y = const_cast<SomeClass*>(x); x->alterState(); } void foo() { SomeClass obj; doShit(&obj); }
Oder wirklich sowas:
void doShit(SomeClass * x) { const SomeClass * cx = const_cast<const SomeClass*>(x); SomeClass * px = const_cast<SomeClass*>(cx); } void foo() { SomeClass obj; doShit(&obj); }
-
beides
-
@Skym0sh0: Verboten ist es, wenn das Objekt selbst (nicht Verweise darauf) als konstant deklariert wurde:
const X x; const_cast<X&>(x).modify();
Compiler dürfen z.B. unter Annahme der Konstanz optimieren -> UB
-
Skym0sh0 schrieb:
volkard schrieb:
const wegcasten funktioniert, wenn vorher const hingecastet wurde.
Was sowas hier heisst?
void doShit(SomeClass const * x) { SomeClass * y = const_cast<SomeClass*>(x); x->alterState(); } void foo() { SomeClass obj;//Zur Laufzeit erstellt im RAM doShit(&obj); }
Oder wirklich sowas:
void doShit(SomeClass * x) { const SomeClass * cx = const_cast<const SomeClass*>(x); SomeClass * px = const_cast<SomeClass*>(cx); } void foo() { SomeClass obj;////Zur Laufzeit erstellt im RAM doShit(&obj); }
genau.
Nexus schrieb:
const X x;//Autsch, kann der Compiler ins ROM legen!!! const_cast<X&>(x).modify();
Unter ROM kann man da vieles verstehen. Vielleicht bei embedded systems in echte ROM-Bausteine rein. Vielleicht unter Windows ins Code-Segment (was vom Prozessor her als schreibgeschützt markiert wird und bei Speichermangel nicht(!) ausgelagert werden muss, sondern einfach aus dem RAM weggeworfen werden kann; bei Bedarf kann man's aus der exe-Datei ja wieder einlagern). Mir egal. Intel/AMD und gcc/MSVC und c++ einigen sich auf was, was verdammt flotten ausführbaren Code macht, ich muss mich nur an den c++-Standard halten. gcc ist oft so eklig, obwohl er eine bestimmte Optimierung tatsächlich gar nicht macht, zu erkennen, daß er sie als fieser Paragraphenreiter machen dürfte, also daß es UB wäre, und daß er den allerschnellsten erlaubten Code erzeugt, mithin alles wegoptimiert.
static char c=0; for(...){ if(!++c) cout<<"ich lebe noch"<<endl;//nur alle 256 läufe eine ausgabe tuwas(...); }
haha, gcc zeigt mir den Effenberg und sagt: Habe Dir nie versprochen, daß nach 127 die -128 kommt, ich optimiere das mal alles fein weg, denn es könnte auf einer hypotetischen Maschine so sein, daß der if-Zweig nie ausgeführt werden würde. Das ist ja allerliebts, daß gcc den schnellsten erlaubten Code erzeugt, echt supi. Aber er soll mir nicht das Wort im Mund herumdrehen. Er tut's aber und ist dabei so gewandt wie wenn Bashar, hustbaer, Arcoth und SeppJ sich zusammentun würden, um mir einen Programmierfehler nachzuweisen.
Ich musste fast alle meine 80er-Jahre-Tricks fallen lassen, so gemein ist der Compiler zu mir. Naja, aber er zaubert supi schnellen Code.
-
es könnte auf einer hypotetischen Maschine so sein, daß der if-Zweig nie ausgeführt werden würde.
Rein aus interesse: Hast du das mal mit -funsigned-char kompiliert?
Und das ist leider immer UB, auch wenn
char
dieselben Werte wieunsigned char
annehmen kann. Er gehört laut Standard eben einfach nicht zu den unsigned integer types.
-
Arcoth schrieb:
Und das ist leider immer UB, auch wenn
char
dieselben Werte wieunsigned char
annehmen kann. Er gehört laut Standard eben einfach nicht zu den unsigned integer types.Das ist nicht immer UB. Der Compiler muss vor dem Compile-Vorgang festlegen, ob char sich verhält wie signed char oder unsigned char. Falls er sich für unsigned char entschieden hat, muss das durchgehen.
-
faragrasenschreiter schrieb:
Der Compiler muss vor dem Compile-Vorgang festlegen, ob char sich verhält wie signed char oder unsigned char.
Der Standard sagt nirgends, dass sich
char
wieunsigned char
verhalten könnte. Er sagt nur, dasschar
dieselben Werte annehmen könnte. Überläufe sind nach wie vor undefiniert, weilchar
eben nicht zu der Gruppe von unsigned integers gehört.Edit: Es könnte natürlich sein, dass der Standard mit "unsigned integer" einfach einen Typ der nur nicht-negative Werte annehmen kann meint... ja, das klingt sogar sehr plausibel...
-
Ok, Ausgangspunkt ist folgender Programmcode:
class Base { public: Base() : _counter(nullptr) {}; void ReferenceCounter(int* counter) { _counter = counter; }; private: int* _counter; }; class Derived : public Base { public: Derived() {}; }; template <class T> class MyClass { public: MyClass(T* p); private: int* _counter; }; template <class T> MyClass<T>::MyClass(T* value) : _counter(new int(1)) { Base* obj = dynamic_cast<Base*>(value); if (obj) { obj->ReferenceCounter(_counter); } } int main() { MyClass<const Derived> myClass2(new Derived()); std::cin.get(); return 0; }
Problem ist das const in der ersten Zeile der main-Funktion.
-
Du kannst auch festlegen, dass das
const
ignoriert wird:template <class T> struct MyClass<T const> : MyClass<T> { using MyClass<T>::MyClass; // Ohne dieses Feature wird es eher hässlich };
-
Dein Fehler ist, dass der
MyClass
-Konstruktor auch Zeiger auf konstante Objekte nimmt, obwohl er Schreibzugriff braucht. Daher entwederstd::remove_const
oder direkt einen neuen Funktionstemplateparameter einführen.Warum
dynamic_cast
für einen einfachen Upcast, der auch implizit funktioniert? So verlagerst du nur Fehler auf die Laufzeit.P.S.: Wenn du einen Smart-Pointer bastelst, kannst du ja mal bei
unique_ptr
(und evtl. sogarshared_ptr
) schauen, wie das gemacht wurde. Für den produktiven Einsatz würde ich von einem eigenen referenzgezählten Smart-Pointer aber abraten und direkt die beiden verwenden...
-
Richtig, es geht um eine eigene SmartPtr implementierung. Darum kommt auch kein std Kram in Frage. Der dynamic_cast ist evtl. überflüssig, aber es können ja auch Objekte die nicht von Base abgeleitet wurden übergeben werden. Ich wollte deshalb nur sicher gehen. Das ganze habe ich mir von shared_ptr abgeschaut. dort passiert folgendes:
template<class Y> explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be complete { boost::detail::sp_enable_shared_from_this( this, p, p ); } template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const { if( weak_this_.expired() ) { weak_this_ = shared_ptr<T>( *ppx, py ); } } template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr<X> const * ppx, Y const * py, boost::enable_shared_from_this< T > const * pe ) { if( pe != 0 ) { pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) ); } } template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr<X> * ppx, Y const * py, boost::enable_shared_from_this2< T > const * pe ) { if( pe != 0 ) { pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) ); } }
Das ganze wollte ich nun auf meine Implementierung übertragen.
-
Enumerator schrieb:
Der dynamic_cast ist evtl. überflüssig, aber es können ja auch Objekte die nicht von Base abgeleitet wurden übergeben werden. Ich wollte deshalb nur sicher gehen.
Einfach nichts tun halte ich für eine schlechte Idee. Wenn du den Smart-Pointer auf von
Base
abgeleitete Klassen auslegst, solltest du eher falsche Benutzung verhindern, z.B. mitstatic_assert
in der Klassendefinition.Enumerator schrieb:
Das ganze habe ich mir von shared_ptr abgeschaut. dort passiert folgendes:
shared_ptr
hat aber einen separaten Reference-Counter, der nicht im Pointee-Objekt eingebaut ist. Daher kann er auch konstante Objekte im Konstruktor annehmen. In deinem Fall solltest du eher das Design überdenken alsconst
leichtsinnig wegzucasten.Wenn du wirklich Reference-Counter innerhalb der Pointee-Objekte willst (wodurch die Benutzerfreundlichkeit sinkt), schau dir
boost::intrusive_ptr
an.