c++arraysc++20unique-ptrdereference

How to dereference std::unique_ptr<int[]>?


The operator* works for std::unique_ptr<std::array<int, 5>> but not for std::unique_ptr<int[]>. But why?

Coming from cppreference:

These member functions are only provided for unique_ptr for the single objects i.e. the primary template.

Here:

#include <vector>
#include <memory>
#include <algorithm>
#include <functional>
#include <fmt/core.h>

int main()
{
    std::vector<int> data { 1, 2, 3, 4, 5 };

    // auto ptr { std::make_unique_for_overwrite<std::array<int, 5> >() };
    auto ptr{ std::make_unique_for_overwrite<int[]>(5) };
    if (ptr == nullptr) return 1;
    auto& out{ *ptr }; // does not compile for <int[]>
    std::ranges::transform(data, std::begin(out), std::negate{});

    for (const auto v : out)
        fmt::print("{} ", v);
    fmt::print("\n");
}

Error message:

<source>:16:17: error: no match for 'operator*' (operand type is 'std::unique_ptr<int [], std::default_delete<int []> >')
   16 |     auto& out { *ptr };
      |                 ^~~~

How can out be made bound to int[5] that is pointed to by ptr? I basically want to make the call to transform compile for the case of using a std::unique_ptr< int[] > ptr;.

One working solution that comes to my mind:

// ...
auto out{ ptr.get() };
std::ranges::transform(data, out, std::negate{});

for (const auto v : std::span{ out, 5 })
// ...

But is there another way of doing this without having to touch raw pointers?


Solution

  • Is there another way of doing this without having to touch raw pointers?

    Your current solution is far optimal in my opinion.

    An alternative might be making a reference-range out of the std::make_unique<int[]> by calling the std::unique_ptr::operator[] on each element.

    #include <ranges>
    #include <memory>
    #include <algorithm>
    
    std::vector<int> data{ 1, 2, 3, 4, 5 };
    auto ptr{ std::make_unique<int[]>(5) };
    
    auto range = std::views::iota(0u, std::size(data))
          | std::views::transform([&ptr](auto idx) -> int& { return ptr[idx]; });
    
    std::ranges::transform(data, std::begin(range), std::negate{});
    
    for (const auto v : range)
        std::cout << v;
    

    See live demo in godbolt.org