Minimal testcase follows:
class Obj {
public:
Obj() { LOG(L"new Obj"); }
~Obj() { LOG(L"del Obj"); }
};
template<class T, template<class> class W> class Emit {
public:
Emit(T* ref) : m_ref(ref) { LOG(L"new Emit"); }
W<T> get() { LOG(L"get from Emit"); return W<T>(m_ref); }
T* m_ref;
};
template<class T> class Wrap {
public:
Wrap() : m_ref(0) { LOG(L"new Wrap(0)"); }
Wrap(T* ref) : m_ref(ref) { LOG(L"new Wrap(T*)"); }
Wrap(Wrap<T>& ref) : m_ref(&ref) { LOG(L"new Wrap(Wrap<T>&)"); ref.m_ref = 0; }
~Wrap() { LOG(L"del Wrap : %p",m_ref); }
operator T*() const { LOG(L"Wrap to T*"); return m_ref; }
Wrap<T>& operator=(T* ref) { LOG(L"Wrap = T*"); m_ref = ref; return *this; } // this will be used, incorrectly, when i use assignment instead
Wrap<T>& operator=(Wrap<T>& ref) { LOG(L"Wrap = Wrap<T>&"); m_ref = ref.m_ref; ref.m_ref = 0; return *this; }
T* m_ref;
};
void wtf() {
Emit<Obj, Wrap> emit(new Obj());
Wrap<Obj> wrap = emit.get();
}
expecting:
new Obj
new Emit
get from Emit
new Wrap(T*) - first formed in Emit::get
new Wrap(Wrap<T>&) - copy constructor (return value optimization removes the second one)
del Wrap : 00000000 - delete temporary object ... content has been transferred
del Wrap : 006F6580 - "wrap" goes out of scope
but actually get:
new Obj
new Emit
get from Emit
new Wrap(T*) - first formed in Emit::get
Wrap to T* - have to downcast/unwrap to use the wrong copy constructor
new Wrap(T*) - using the wrong copy constructor
del Wrap : 006F6580 - delete temporary object ... this is bananas
Wrap to T* ???
new Wrap(T*) ??? going bananas twice, presumably first at "Wrap<Obj> get() { return Wrap<Obj>(ref); }" and then "Wrap<Obj> = emit.get();"
del Wrap : 006F6580 ??? given that it goes bananas - no return value optimization is done with Debug nor Release target.
del Wrap : 006F6580 - "wrap" goes out of scope
What am i missing? Can i force it to be sensible?