Question

I have an easy question about shared pointers and move semantics. Imagine that I have a class with a private member variable like this:

class C
{
private:
    std::shared_ptr<std::vector<uint8_t>> buffer;
}

I need to provide public getters and setters. The getter seems obvious:

std::shared_ptr<std::vector<uint8_t>> C::GetBuffer()
{
    return buffer;
}

However, being new to C++ I'm having trouble writing the setter. I could do something like this:

void C::SetBuffer(std::shared_ptr<std::vector<uint8_t>> input)
{
    buffer = input;
}

However that results in a copy of input to buffer, but I don't really want the caller to have shared ownership. Instead I want to move the data. I tried to solve this with:

void C::SetBuffer(std::shared_ptr<std::vector<uint8_t>>& input)
{
    buffer(std::move(input));
}

But this is an error: "call of an object of a class type without appropriate operator() or conversion functions to pointer-to-function type."

Can somebody help me understand:
1. What is going on here?
2. How to best implement the setter?

Was it helpful?

Solution

You can fix the error you're getting by writing this:

void C::SetBuffer( std::shared_ptr<std::vector<uint8_t> > &input ) {
    buffer = move(input);
}

This will call shared_ptr's move-assignment operator, which will pilfer input. However, this won't really stop the caller from having shared ownership. Once you accept (or dispense) a shared_ptr from/to an unknown client, you don't have much in the way of control about who shares ownership. Even if input is pilfered, there's no reason to expect that input was the only copy of the shared_ptr you just received. If, for example, the function that called SetBuffer() took whatever became input from its caller by value, that higher-level copy of the pointer will continue to share ownership.

Note that your getter has a similar issue. You're returning a shared_ptr to your own internal object (and what's more, it's a shared_ptr-to-non-const, so the client can modify the shared state) and wherever that shared_ptr gets passed around after you provide it, those copies will also share (mutable) ownership.

If you really want to ensure you have exclusive ownership, you can hold a unique_ptr instead of a shared_ptr and have your getter pass back a const-reference, and your setter take either a unique_ptr or a value.

OTHER TIPS

If your goal is to allow a caller to pass sole ownership of a buffer to your object, you should accept it by unique_ptr instead of shared_ptr:

void C::SetBuffer(std::unique_ptr<std::vector<uint8_t>> input)
{
    buffer = std::move(input);
}

Rvalue unique_ptr is convertible to shared_ptr for exactly this purpose.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top