0

Is array/vector of unique_ptr guaranteed to be array of raw pointers internally. It seems like it is so on my machine, but does the standard guarantee it?

I guess the question can be reduced to: Does unique_ptr always remain in memory as only a raw pointer or it's possible to have other metadata saved with it.

Basically i need to call a c function which takes an array of pointers (Actually it takes a double pointer obviously). Now I can pass it a vector (vector::data) of unique_ptr with proper casting and it works fine. But I want to ensure that it will remain so on all the implementation of c++.

using item_ptr = unique_ptr<ITEM, ITEM_destructor>;
vector<item_ptr> items;
set_menu_items(m, reinterpret_cast<ITEM **>(items.data()));
4
  • 3
    Type punning in general is undefined behavior. Using it to access the underlying pointer type is asking for trouble. What is wrong with allocating a new vector and copying the raw pointers into it? Commented Jun 12, 2023 at 3:57
  • 1
    No, the behavior of your program is undefined. Commented Jun 12, 2023 at 3:57
  • @paddy These raw pointers have to be freed by calling a custom function. If I add them to vector directly I will have to manually free them in the end. Commented Jun 12, 2023 at 4:13
  • @paddy Okay you mean keep two vectors, one containing unique_ptr and other the raw ptrs. That would work. Thanks. Good idea. Commented Jun 12, 2023 at 4:17

1 Answer 1

2

Never keep double administration and do not to use reinterpret_cast in your code!. There should only be one source of information. This one source will be your std::vector<std::unique_ptr<Item>>. You only need a way to temporarily have a Item** to be able to pass this (as a view) to your set_menu_items call.

For this example I assume the list of items will NOT change after the call to set_menu_items.

#include <vector>
#include <memory>
#include <iostream>
#include <string>


// demo item
struct Item
{
    std::string name;
};

// function accepting only raw pointers
void set_menu_items(Item** items, const std::size_t number_of_items)
{
    for (std::size_t n{0ul}; n < number_of_items; ++n)
    {
        Item* item_ptr = items[n];
        std::cout << item_ptr->name << "\n";
    }
};

// a struct to hold raw pointers for a while
class temporary_item_pointers_t final
{
public:
    explicit temporary_item_pointers_t(const std::vector<std::unique_ptr<Item>>& item_ptrs)
    {
        for (const auto& ptr : item_ptrs)
        {
            m_item_ptrs.push_back(ptr.get());
        }
    }

    Item** get() noexcept
    {
        return m_item_ptrs.data();
    }

    auto size() const noexcept
    {
        return m_item_ptrs.size();
    }

private:
    std::vector<Item*> m_item_ptrs;
};


auto make_items(const std::vector<std::string>& names)
{
    std::vector<std::unique_ptr<Item>> items;
    for (const auto& name : names)
    {
        items.emplace_back(std::make_unique<Item>(name));
    }
    return items;
}


int main()
{
    auto items = make_items({ "item 1", "item 2", "item 3" });
    temporary_item_pointers_t item_ptrs{ items };
    set_menu_items(item_ptrs.get(), item_ptrs.size());

    return 0;
}
Sign up to request clarification or add additional context in comments.

1 Comment

I will make this the accepted answer. Although paddy said basically the same thing. He doesn't seem to be interested in making it an answer; and this is more informative and detailed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.