user1235183
user1235183

Reputation: 3072

how to change memory guarded by std::shared_ptr(unique_ptr)

I want to use unique and shared_ptr to take care of allocated memory. But as far as I can see the access to the memory(get,...) is always const and return const pointer so I cannot manipulate the memory stored.

EXAMPLE:

std::unique_ptr<int[]> ptr( new int[42]);

memset(ptr.get(),0,42*sizeof(int)); //not possible ptr.get() returns const pointer

Is there a work around? If there is no way to manipulate the memory, what are the design reasons to not offer such an interface?

Upvotes: 2

Views: 1928

Answers (2)

Richard Hodges
Richard Hodges

Reputation: 69882

Now that the mechanics of std::unique_ptr<>::get() are understood, you may see some value in a more idiomatic algorithm-based approach.

Arguably it's more readable, avoids casts, will work should you change your mind about the underlying storage and is just as efficient.

some examples:

std::fill(ptr.get(), ptr.get() + 42, 0);

std::fill(&ptr[0], &ptr[42], 0);

std::fill(std::addressof(ptr[0]), std::addressof(ptr[42]), 0);

and with a little boilerplate:

for (auto& x : linear_range(ptr.get(), 42)) {
  x = 0;
}

With optimisations enabled, all of these evaluate to the same efficient code.

boilerplate is here:

template<class I1, class I2>
struct range_impl {

  auto begin() const { return i1; }
  auto end() const { return i2; }
  I1 i1;
  I2 i2;
};

template<class I1, class I2>
auto range(I1 i1, I2 i2) {
  return range_impl<I1, I2> { i1, i2 };
}

template<class I1>
auto linear_range(I1 i1, std::size_t length) {
  return range(i1, std::next(i1, length));
}

Upvotes: 2

PeterT
PeterT

Reputation: 8284

The function signature for std::unique_ptr::get is

pointer get() const;

the type of pointer being "std::remove_reference<Deleter>::type::pointer if that type exists, otherwise T*"

The only const in that signature being for the function itself, so that it can be called on a const std::unique_ptr<int> a;

So your premise of and return const pointer is not true. You can change the memory pointed to by a smart pointer via a pointer retrieved from its get() member function.

Additionally memset(ptr.get(),0,42*sizeof(int)); is also fine and doesn't need any casts because you can still implicitly convert any pointer to a void pointer according to this pointer-conversion rule:

A prvalue pointer to any (optionally cv-qualified) object type T can be converted to a prvalue pointer to (identically cv-qualified) void. The resulting pointer represents the same location in memory as the original pointer value. If the original pointer is a null pointer value, the result is a null pointer value of the destination type.

So, your code has no issues and seems to be compliant with the standard.

Upvotes: 4

Related Questions