ausdruck durch konstanten ausdruck ersetzbar?
-
für zwei beliebige klassen T und U, wobei U direkt oder mittelbar abgeleitet ist (aber nicht virtuell), ist für einen pointer u auf ein beliebiges (unter-)objekt vom typ U:
T* t = p; prtdiff_t diff = reinterpret_cast< char* >( u ) - reinterpret_cast< char* >( t );diff eine konstante, die der compiler kennt (andernfalls wäre static_cast in downcasts grundsätzlich unsicher). gibt es eine möglichkeit, diff as konstanten ausdruck zu erhalten (ich hatte an offsetof gedacht, aber die basis klasse ist ja kein echter member)?
-
Das geht nicht!
Es sei denn T und U sind Normalteiler
bzw. polymorph zueinander!
-
Das klingt ziemlich brutal und ich frage mich, was du damit vor hast

Rein theoretisch sollte der Ausdruck aber schon zur Compilezeit konstant sein für zwei vollständig benannte Klassen T und U. Wie man diesen aber legal bekommt, geschweige denn legal verwendet, wüsste ich aber nicht.
-
sigridweyl schrieb:
Das geht nicht!
Es sei denn T und U sind Normalteiler
bzw. polymorph zueinander!ich sagte bereits, dass U direkt oder mittelbar von T abgeleitet sein soll. T und U müssen keineswegs polymorph sein, damit eine wandlung von U* nach T* legal ist (auch wenn das in dem code, um den es mir geht, eine voraussetzung ist).
und ja es ist brutal, es geht hier auch um die (möglicherweise ganz evtl
mögliche) optimierung eines templates, genauer den auto_ptr, den ich hier:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-127044.html
gebastelt habe. in diesem falle ist auto_ptr_ref zwar sicher aber auch im allgemeinen sehr ineffizient. das kann man für zumindest für den fall, dass keine konvertierung stattfindet, optimieren (die signatur ist dann nicht mehr ganz standard-konform, an der nutzbarkeit des templates ändert sich aber nichts):template<typename Target, bool same_type = false> class auto_ptr_ref { template<typename> friend class auto_ptr; explicit auto_ptr_ref(void** pptr, Target* ptr) throw() : pptr_( pptr ), ptr_( ptr ) {} Target* release() const { *pptr_ = 0; return ptr_; } void** pptr_; Target* ptr_; }; template<typename Target> class auto_ptr_ref< Target, true > { template<typename> friend class auto_ptr; explicit auto_ptr_ref(void** pptr, Target*) throw() : pptr_( pptr ) {} Target* release() const { Target* ptr = static_cast< Target* >( *pptr_ ); *pptr_ = 0; return ptr; } void** pptr_; }; // ... auto_ptr(auto_ptr_ref< Pointee, false > rhs) throw() : ptr_( rhs.release() ) {} auto_ptr(auto_ptr_ref< Pointee, true > rhs) throw() : ptr_( rhs.release() ) {} // ... template<typename Target> operator auto_ptr_ref< Target, boost::is_same< Target, Pointee >::value >() throw() { // require complete type here // (implies that Target is complete or else conversion fails anyway) (void)sizeof Pointee; // has_virtual_destructor works only with VC8, is_polymorphic is a weaker requirement BOOST_STATIC_ASSERT( ( \ boost::is_same< Target, Pointee >::value \ || boost::is_polymorphic< Target >::value \ && boost::has_virtual_destructor< Target >::value ) ); return auto_ptr_ref< Target, boost::is_same< Target, Pointee >::value >( &ptr_, get() ); } // etc...wenn es gelänge, die offset-verschiebung beim kompilieren zu ermitteln (oder wenigstens alle fälle, in denen keine verschiebung stattfindet, insbesondere der gewöhnliche single-inheritance fall) - könnte die optimierung auf diese fälle erweitert werden.
-
nocheinmal zur motivation:
der auto_ptr von vc8 hat einen tödlichen defekt, wie ich schon angedeutet habe,
in vc7.1 ist der auto_ptr komplett kaputt was zuweisung von kompatiblen rvalues angeht.beiden vc++ versionen als auch alle gcc versionen (d.h. die standardbibliothek, die jeweils dazu gehört) kommen nicht mit mehrfachvererbung zurecht:
#include <iostream> #include <memory> struct A { virtual ~A() {} int i; }; struct B { virtual ~B() {} int i; }; struct C : A, B { C() { A::i = 1; B::i = 2; } }; using namespace std; auto_ptr< C > foo() { return auto_ptr< C >( new C ); } int main() { C* c = new C; A* a = c; B* b = c; cout << "raw: " << a->i << " " << b->i << endl; delete c; auto_ptr< A > aa( foo() ); cout << "auto: " << aa->i << " "; auto_ptr< B > ab( foo() ); cout << ab->i << endl; }eine implementierung, die funktioniert, ist z.b. die von STLport, dort wird auch ein doppel-pointer ansatz verfolgt. allerdings ist dieser ansatz soboptimal im gewöhnlichen fall. zu erwähnen wäre noch, dass diese optimierung tatsächlich eine rolle spielt - z.b. wird visual c++ eine funktion, die ein objekt mit nicht-trivialen destruktor - wie z.b. auto_ptr - zurückgibt, niemals inlinen (wenn C++ exceptions erlaubt sind), was zur folge hat, dass auch das auto_ptr_ref objekt nicht eliminiert werden kann. jede vereinfachung dieses proxies erhöht die qualität das endgültigen assembler codes.
-
ich weiß nicht, was du dir erwartest?
als ausgabe bekomme ichprogramm schrieb:
raw: 1 2
auto: 1 2
-
mit g++, ja? ok, ich hab noch mal in die quellen geschaut - es klappt tatsächlich mit gcc, was mehrfachvererbung angeht. allerdings ist das ganze nicht mehr standardkonform, denn:
int main() { auto_ptr<C> ac(foo()); ac.operator auto_ptr_ref<C>(); cout << ac->A::i; // oops }zugegeben, sowas schreibt man ja nicht, aber es ist durchaus zulässig.