Ternary operator and ScopedPointer


#1

I found something weird. If you assign to a normal pointer the ScopedPointer’s pointer nothing happens to the ScopedPointer, but if you do it with a ternary operator something goes wrong and ScopedPointer will be the null pointer:

[code] ScopedPointer m_comp;
m_comp = new Component();

Component* c1 = m_comp; // All ok
Component* c2 = true? m_comp : 0; // m_comp here will be a null pointer
[/code]

In this simple example, c1 work as expected, it’s just a copy of m_comp, and m_comp will keep its value. However c2 will “steal” the memory address of m_comp, and m_comp will be the null pointer


#2

Ah, the intricacies of c++!

The ternary op must be effectively doing this:

…and creating an temporary intermediate copy which steals the pointer from your original one.

Obviously you could write m_comp.get() in your code to make it work correctly, but it’s really annoying that it’s possible to make a slip like that without any warning. And I can’t think of anything I could do that would automatically catch this kind of mistake - very annoying!


#3

Yes, that’s what I did while I was waiting for an answer. Thanks you :wink:


#4

You can avoid this by 2 means:

  1. Prevent implicit construction from 0 in ScopedPtr class (so the compiler can not select “test ? ScopedPtr : ScopedPtr” version, but “test ? Ptr : Ptr” version).
    Write a private TEMPLATE ScopePtr constructor taking an “int” for example. Since SFINAE this constructor will not be selected and the ternary overload matching will fail and goes to the next one.
    The downside of this is ScopePtr ptr(0) will no longer work. (Maybe it could work with an explicit constructor instead, I don’t know).

  2. Detect the “temporary” and act differently. Some sort of reference counting could be used here, or using the excellent ScopeGuard’s idea from Alexandrescu. This implies rewriting the ScopePtr class.


#5

Yes, I’m aware of that tricks but I think it’d break a whole heap of existing code.

It would work nicely with the new C++11 nullptr_t type though - I should probably make it work like that if the compiler supports it.