In my case T
is pcl::PointCloud<pcl::PointXYZ>>
but the question shoud stand for any type T
. The following example produces an error:
using pc = pcl::PointCloud<pcl::PointXYZ> >;
boost::shared_ptr<pc> p(new pc);
boost::shared_ptr<const pc> const_p(new pc);
// This is legal
const_p = p;
// The atomic equivalent is not
boost::atomic_store(&const_p, p);
The problem is that the boost::atomic_store
expects both arguments to be T*
and T
, but these are considered different types despite the fact that it's perfectly safe to assign p
to const_p
. The following doesn't work either.
boost::atomic_store(&const_p, const_cast<boost::shared_ptr<const pc> > (p));
Despite the above basically casting a pc*
to const pc*
which is perfectly safe, it produces an error about const_cast
not being able to convert to different type. I understand that because pc
is a template argument, it is considered part of the type of the shared_ptr
and not a cv qualification. The following work
boost::atomic_store(&const_p, boost::shared_ptr<const pc>(p));
However, it creates an extra unecessary boost::shared_ptr
. It is my understanding that the same is true for boost::const_pointer_cast<const pc>(p)
This can be avoided if p
is no longer needed.
boost::atomic_store(&const_p, boost::shared_ptr<const pc>(std::move(p));
This still creates an extra object but it shouldn't matter because the reference count is not modified, which is the expensive part of copying a shared_ptr
on account of being atomic.
It just so happens that this occurs in a non-critical part of my code so I'm fine with the above, but I would like to know for future reference: If std::move
was not an option, how would one atomically store a boost::shared_ptr<T>
to a boost::shared_ptr<const T>
without the overhead of creating an unecessary temporary pointer? It should be possible because it is safe to view a T
through a const T*
, but I can't figure out a way to do it.
I understand that because pc is a template argument, it is considered part of the type of the shared_ptr and not a cv qualification.
Yes, this is known as "non-deducible context".
The following work
boost::atomic_store(&const_p, boost::shared_ptr<const pc>(p));
However, it creates an extra unecessary
boost::shared_ptr
. It is my understanding that the same is true forboost::const_pointer_cast<const pc>(p)
This can be avoided if p is no longer needed.
Well, surprise, you always get the copy:
template<class T> void atomic_store( shared_ptr<T> * p, shared_ptr<T> r ) BOOST_SP_NOEXCEPT
{
boost::detail::spinlock_pool<2>::scoped_lock lock( p );
p->swap( r );
}
Note the second parameter is by value. This, at once, solves the mystery:
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/atomic.hpp>
namespace pcl {
struct PointXYZ {};
template <typename P> struct PointCloud {
};
}
int main() {
using pc = pcl::PointCloud<pcl::PointXYZ>;
boost::shared_ptr<pc> p = boost::make_shared<pc>();
boost::shared_ptr<const pc> const_p = boost::make_shared<pc>();
// This is legal
const_p = p;
// The atomic equivalent is too
boost::atomic_store<pc const>(&const_p, p);
}
If std::move was not an option, how would one atomically store a boost::shared_ptr to a boost::shared_ptr without the overhead of creating an unecessary temporary pointer?
You can't. Look at it this way: load/store are meant to be trivial operations amenable to atomic lockfree implementations. They do 1 thing, and they do it well¹.
Doing implicit conversions is just not the responsibility of that function.
I'd suggest using a wrapper function, or even using ADL to resolve your own overload from your own namespace.