c++boostboost-rangeboost-adaptors

Why can't I use boost::adaptor::map_values outside a ranged-based loop?


I want to use boost's boost::adaptor::map_values to construct a vector of all values of a std::map. Take this example code (or try it at Godbolt):

#include <boost/range/adaptors.hpp>
#include <boost/range/adaptor/map.hpp>

std::vector<int> getValueVector(const std::map<int, int> & m)
{
    auto valueRange = boost::adaptors::map_values(m);
    return std::vector<int>(valueRange.begin(), valueRange.end());
}

int main() {
    std::map<int, int> my_map;
    getValueVector(my_map);
}

GCC complains:

error: no match for call to '(const boost::range_detail::map_values_forwarder) (const std::map<int, int>&)'

All examples and all documentation that I can find only ever use boost adaptors inside statements that accept ranges (like range-based for loops or boost::copy), never in statements that need pairs of iterators like std::vector's constructor.

According to GCC, the problem here is that map_values is not applicable to std::map<int, int>. However, this is literally the only example given by the docs, so I'm pretty sure that std::map<int, int> should be okay for map_values.

Whas is amiss here?

P.S.: I know there are a bazillion other ways of elegantly constructing a vector from the values of a map. I want to know why this code does not work. Also, this approach has the added benefit of guaranteed RVO instead of "probably NRVO", which most of the other solutions that I can think of have.


Solution

  • I am not familiar with ranges, but this might be an answer, so I post it as answer.

    At the top they write:

    Syntax      Code
    
    Pipe        rng | boost::adaptors::map_values
    Function    boost::adaptors::values(rng) 
    

    And indeed when I change your code to this:

    #include <boost/range/adaptors.hpp>
    #include <boost/range/adaptor/map.hpp>
    #include <iostream>
    
    std::vector<int> getValueVector(const std::map<int, int> & m)
    {
        auto valueRange = boost::adaptors::values(m);
        return std::vector<int>(valueRange.begin(), valueRange.end());
    }
    
    int main() {
        std::map<int, int> my_map{{1,2},{3,4},{5,6}};
        auto v = getValueVector(my_map);
        for (const auto& e : v) std::cout << e << " ";
    }
    

    There is the expected output:

    2 4 6