Search code examples
c++optimizationcopycloneshared-ptr

Get a vector of map keys without copying?


I have a map of objects where keys are std::string. How can I generate a vector of keys without copying the data?

Do I need to change my map to use std::shared_ptr<std::string> as keys instead? Or would you recommend something else?

My current code goes like this:

MyClass.h

class MyClass {
private:
    std::map <std::string, MyType> my_map;
public:
    const std::vector<std::string> MyClass::getKeys() const;
}

MyClass.cpp

const std::vector<std::string> MyClass::getKeys() const
{
    std::vector<std::string> keys = std::vector<std::string>();
    for (const auto& entry : my_map)
        keys.push_back(entry.first); // Data is copied here. How can I avoid it?
    return keys;
}

Solution

  • As suggested by Kevin, std::views::keys was pretty much made for this. The view it produces is a lightweight object, not much more than a pointer to the range argument, which solves the ownership and lifetime issues. Iterating over this view is identical to iterating over the map, the only difference is that the view's iterator dereferences to the first element of the underlying map value.

    As for some code, it is pretty simple:

    // ...
    #include <ranges>
    
    class MyClass {
    private:
        std::map<std::string, MyType> my_map;
    public:
        std::ranges::view auto getKeys() const;
    };
    
    std::ranges::view auto MyClass::getKeys() const
    {
        return std::views::keys(my_map);
    }
    

    Since ranges and concepts go hand in hand, I've used the std::ranges::view to constrain the auto return type. This is a useful way to let the compiler and user of your function know that it will return a particular category of object, without having to specify a potentially complicated type.