Составные ключи std::map с++

У меня есть картеж/структура из 3 чисел, по нему производится лексеграфическое сравнение, могу ли я в std::map получить диапазон всех картежей, которые начинаются c определенного числа, к примеру (1,2,3),(1, 3, 2)... Я понимаю что std::map не поддерживает составные ключи, но может быть есть хитрый способ перегрузить компаратор, но в голову не приходит как это сделать.


Ответы (2 шт):

Автор решения: Chorkov
struct tuple_less
{
    using is_transparent = int;

    template< typename...T1, typename ... T2 >
    bool operator() ( const std::tuple<T1...>& lhs,
                      const std::tuple<T2...>& rhs ) const
    {
        // выделяем общее начало кортежей
        constexpr size_t min_size = std::min( sizeof...(T1), sizeof...(T2) );
        auto l_tie = sub_tie( lhs, std::make_index_sequence< min_size >{} );
        auto r_tie = sub_tie( rhs, std::make_index_sequence< min_size >{} );

        // if(l_tie<r_tie) return true;
        // if(r_tie<l_tie) return false;
        // Более короткая строка меньше более более длинной, (если начало одинаконо).
        // Но, равные по длинне строки - равны
        // return sizeof...(T1) < sizeof...(T2);

        // Оптимизированно:
        if(sizeof...(T1) < sizeof...(T2))
            return !(r_tie<l_tie);
        else
            return l_tie<r_tie;
    }

private:
    template< typename T, size_t ... i  >
    static auto sub_tie( const T& tuple, std::index_sequence<i...> ) {
        return std::tie( (std::get<i>(tuple))... );
    }
};

// тест
int main()
{
    std::tuple<int,int,int> a1 = {1,2,3};
    std::tuple<int,int> a2 = {1,2};
    assert( tuple_less{}(a1,a2) == false );
    assert( tuple_less{}(a1,a1) == false );
    assert( tuple_less{}(a2,a1) == true  );


    using key_type=std::tuple<int, unsigned, std::string>;

    std::map< key_type, std::string, tuple_less > map={
        { { 1, 2, "12"}, "x"},
        { { 1, 5, "15"}, "yy"},
        { { 2, 0, "20"}, "zzz"},
    };

    auto begin = map.lower_bound( std::tuple<int>{1} );
    auto end = map.lower_bound( std::tuple<int>(1+1) );
    for( auto i=begin; i!=end; ++i)
    {
        std::cout<<i->second<<std::endl;
    }
}
→ Ссылка
Автор решения: HolyBlackCat

В качестве типа ключа можно использовать std::array<int, 3> или std::tuple<int, int, int>. Или, если у полей есть осмысленные названия (а не просто индексы), то свою структуру с перегруженным оператором <.

Дальше, красивее, конечно, сделать свой компаратор, как в ответе @Chorkov.

Но, чтобы не заморачиваться, можно просто вызвать .lower_bound({n,0,0}) чтобы найти начало диапазона, и .upper_bound({n+1,0,0}) чтобы найти конец диапазона.

→ Ссылка