michael michael - 3 months ago 5
C++ Question

GCC converts private inherited to parent

I was trying to wrap boost intrusive ptr with my own ptr which overloads the casting to bool. In addition, in my ptr I would like to restrict access to some of the boost intrusive ptrmethods. At first I used boost intrusive ptr as a private member of my ptr and used the bool idiom, and it worked well and used the bool idiom.
At some point from my own special reasons I needed the new ptr to be type of boost intrusive ptr, so I used private inheritance, but that didn't work. From some reason gcc tries to handle my ptr as a boost intrusive ptr and refuses to use the publicly exposed bool idiom.
Here is a sample code which demonstrates it:

#include <boost/intrusive_ptr.hpp>
#include <stdio.h>

class Object
{
public:
Object() : _refCount(0) {}
void addRef() { _refCount++; }
unsigned int releaseRef() { return --_refCount; }

private:
unsigned int _refCount;
};

inline void intrusive_ptr_release(Object* p)
{
if (p->releaseRef() == 0)
{
delete p;
}
}

inline void intrusive_ptr_add_ref(Object* p)
{
p->addRef();
}

// Option A: using private member
template<class ObjectT>
class PtrA
{
public:
explicit PtrA(ObjectT* obj) : _obj(obj) {}
PtrA() {}

ObjectT* operator->() { return _obj.operator->(); }
const ObjectT* operator->() const { return _obj.operator->(); }

ObjectT* get() { return _obj.get(); }
const ObjectT* get() const { return _obj.get(); }

typedef const ObjectT* (PtrA::*unspecified_bool_type)() const;

operator unspecified_bool_type() const
{
if (_obj != NULL)
{
printf("ptr is not null\n");
}

return _obj.get() == 0 ? 0: (unspecified_bool_type)&PtrA::get;
}

private:
boost::intrusive_ptr<ObjectT> _obj;
};

// Option B: using private inheritance
template<class ObjectT>
class PtrB : private boost::intrusive_ptr<ObjectT>
{
public:
explicit PtrB(ObjectT* obj) : boost::intrusive_ptr<ObjectT>(obj) {}
PtrB() {}

using boost::intrusive_ptr<ObjectT>::operator->;
using boost::intrusive_ptr<ObjectT>::get;

typedef const ObjectT* (PtrB::*unspecified_bool_type)() const;

operator unspecified_bool_type() const
{
const ObjectT* p = boost::intrusive_ptr<ObjectT>::get();
if (p != NULL)
{
printf("ptr is not null\n");
}

return p == 0 ? 0 : (unspecified_bool_type)&PtrB::get;
}
};

int main()
{
// this verison compiles
// PtrA<Object> obj(new Object());

PtrB<Object> obj(new Object());

if (obj == NULL)
{
printf("object is null\n");
}
if (!obj)
{
printf("object is null\n");
}
return 0;
}


Using PtrB in this code results in a compilation error:

g++ -std=c++11 ./test.cpp
...
./test.cpp: In function 'int main()':
./test.cpp:88:16: error: 'boost::intrusive_ptr<Object>' is an inaccessible base of 'PtrB<Object>'
if (obj == NULL)
^
In file included from /usr/include/boost/smart_ptr/intrusive_ptr.hpp:167:0,
from /usr/include/boost/intrusive_ptr.hpp:16,
from ./test.cpp:1:
/usr/include/boost/smart_ptr/detail/operator_bool.hpp:60:10: error: 'bool boost::intrusive_ptr<T>::operator!() const [with T = Object]' is inaccessible
bool operator! () const BOOST_NOEXCEPT
^
./test.cpp:92:10: error: within this context
if (!obj)
^
./test.cpp:92:10: error: 'boost::intrusive_ptr<Object>' is not an accessible base of 'PtrB<Object>'


If boost intrusive ptr is privately inherited, what does GCC tries to use it instead the public bool idiom?
I use gcc 4.8.5

Answer

If boost intrusive ptr is privately inherited, what does GCC tries to use it instead the public bool idiom?

Private inhertiance doesn't change the rules of name lookup. It just makes the resulting best viable candidate ill-formed if it requires an inaccessible conversion. In this case, you're doing these operations:

if (obj == NULL)
if (!obj)

neither of those operations goes through your operator unspecified_bool_type(). The former finds:

template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, U * b);

And the latter finds intrusive_ptr<T>::operator!(). Those are the best viable candidates for those two operations. Your operator would be a user-defined conversion sequence but the ones that intrusive_ptr provides are standasrd conversion sequences. But these two require a conversion from PtrB<T> to intrusive_ptr<T>, which is what makes them ill-formed.

Comments