I cannot figure this out.. Looks like I'm missing something simple?
What do I put in MakePointToSameValue
so that at point (1)
- both b.ptr and c.ptr point to the same as a.ptr
- in other words,
a.ptr.get() == b.ptr.get() == c.ptr.get()
- the value originally pointed to by b.ptr and c.ptr gets deleted
?
struct Test
{
public:
Test( int val ) :
ptr( std::make_shared< int >( val ) )
{
}
void MakePointToSameValue( Test& other )
{
//what do I put here?
//other.ptr = this->ptr; //doesn't do it
}
private:
std::shared_ptr< int > ptr;
};
Test a( 0 );
Test b( 5 );
Test c( b );
b.MakePointToSameValue( a );
//(1)
Copying the ptr does not work, since it does not alter c (well, c.ptr has it's refcount decreased by one). Note that I use int
just for simplicty, but it should work for noncopyable types.
Why? I have a class representing values, any type of values, for use in a sort of compiler. The actual value, nor how it is stored, is known when it gets instantiated. The only thing known is the type. So the class stores a shared_ptr containing a placeholder for a value determined later on (corresponds to compiling the arguments of a function definition when passed as a pointer or reference: the compiler only knows type, nothing more). At runtime the placeholder should be replaced by an actual value.
Edit fresh start of the day, came up with this. I knew it was simple.
void MakePointToSameValue( Test& other )
{
other.ptr.swap( ptr );
ptr.reset();
ptr = other.ptr;
}
Additional question now is: will the above work as expected for any standard compliant pointer?
You need two levels of indirection here. While you're right that all
shared_ptr
objects point to a common metadata block that contains the count and the pointer to the actual value, if you tried to update that block to point to a different object, you'd now have two metadata blocks pointing to the same value, each with their own different idea of what the reference count is. There's the right number (in the sense that it matches the reference count) ofshared_ptr
objects using each metadata block, so the count on each block will eventually reach zero, but there's no way to know which block is the last block to reach a count of zero (and hence should delete the value). Soshared_ptr
sensibly doesn't allow changing the object pointer inside the metadata. You can only associate theshared_ptr
with a new metadata block, new count, new object. And other pointers to the same object aren't affected.The right way to do this is to use a second layer of indirection (
shared_ptr<shared_ptr<int> >
). That way there's exactly one metadata block and exactly one count for each object. Your update takes place to the intermediate shared_ptr.Well, as far as I understand your requirements,
shared_ptr
does not contain any such mechanism; nor would any regular type. If you want this behaviour, you'll have to code it yourself. My suggestion: add a private staticstd::list<std::weak_ptr<Test>> registry
; register eachTest
instance by adding it to theregistry
list in the constructor, and make sure to remove it in the destructor.Then use that registry in
MakePointToSameValue
to iterate through all instances and reset the value ofptr
.If you're interested in efficiency and have a bit more instances than three, you'll want to replace the
list
with anunordered_set
; and perhaps useunique_ptr
rather thanshared_ptr
in yourTest
class.Answer to additional question: no, it won't work. Look at the documentation of
reset()
: it resets one particular instanceshared_ptr
: it does nothing with (and knows nothing of) any other instances. When the reference count in the control block reaches zero, it additionally destroys the pointee, but that's it.