Пользовательская сортировка вектора кортежей

У меня есть вектор кортежей типа

vector<tuple<T1, T2, T3>> v;

Я считаю, что когда сравнение по умолчанию запускается для типов кортежей, оно выполняет лексикографическое сравнение.

Могу ли я выполнять сравнения по элементу, который я выбираю? Например, вторым элементом в приведенном выше примере или i-м элементом в кортеже, содержащем m типов?

Спасибо заранее.

Ответ 1

Есть много способов сделать это, тот, который я использую, сводится к объявлению пользовательского объекта сравнения, на самом деле следующее

// Functor to compare by the Mth element
template<int M, template<typename> class F = std::less>
struct TupleCompare
{
    template<typename T>
    bool operator()(T const &t1, T const &t2)
    {
        return F<typename tuple_element<M, T>::type>()(std::get<M>(t1), std::get<M>(t2));
    }
};

Он работает для кортежей произвольной длины (избегая вариационных шаблонов - хотя это довольно легко и безопаснее сделать это вариативным способом, потому что вы можете объявлять аргументы оператора() как кортежи произвольной длины), а также работает для пар и , вы можете передать ему пользовательскую функцию сравнения/объект, но в качестве политики по умолчанию использует оператор <. Пример использования будет

int main()
{
    vector<tuple<int, string>> v;
    v.push_back(make_tuple(1, "Hello"));
    v.push_back(make_tuple(2, "Aha"));

    std::sort(begin(v), end(v), TupleCompare<0>());
    return 0;
}

существует более современный способ, используя лямбды, поэтому строка сортировки будет

std::sort(begin(v), end(v), 
    [](tuple<int, string> const &t1, tuple<int, string> const &t2) {
        return get<0>(t1) < get<0>(t2); // or use a custom compare function
    }
);

Я считаю, что стоит сделать объект функции для этого (избегает много кода шаблона) и идти с первым подходом


ИЗМЕНИТЬ

Поскольку комментарий Yakk (на С++ 1y) стал стандартным, совместимым (С++ 14), ниже мы приводим пример для generic lambdas

std::sort(begin(v), end(v), [](auto const &t1, auto const &t2) {
        return get<0>(t1) < get<0>(t2); // or use a custom compare function
});

который в значительной степени соответствует механике TupleCompare, так как шаблон operator() также исчисляется.

Ответ 2

Вы можете сделать это так:

#include <tuple>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

vector<tuple<int, float, char>> v;

int main() {
  v.push_back(std::make_tuple(1,1.2,'c'));
  v.push_back(std::make_tuple(1,1.9,'e'));
  v.push_back(std::make_tuple(1,1.7,'d'));

  sort(v.begin(),v.end(),
       [](const tuple<int,float,char>& a,
       const tuple<int,float,char>& b) -> bool
       {
         return std::get<1>(a) > std::get<1>(b);
       });

  cout << std::get<2>(v[0]) << endl;
  return 0;
}

Ответ 3

Да, вы можете определить пользовательскую сортировку на С++, когда вам это нужно. Я предполагаю, что вам нужно это для std::sort, правильно? Посмотрите документацию std::sort, во второй версии алгоритма, если быть точным, тот, который принимает аргумент comp.

Вы должны определить менее функтор, что-то вроде этого:

struct CustomLessThan
{
    bool operator()(tuple<T1, T2, T3> const &lhs, tuple<T1, T2, T3> const &rhs) const
    {
        return std::get<1>(lhs) < std::get<1>(rhs);
    }
};

И затем используйте его в std::sort:

std::sort(v.begin(), v.end(), CustomLessThan());

В С++ 11 вы можете сделать код намного короче, используя lambda вместо создания именованной структуры. Примеры, приведенные на cppreference.com, также показывают эту технику.

Ответ 4

Итак, вот самый простой способ узнать, как это сделать (и я думаю, что это еще более прямолинейно, чем основной ответ). Это будет работать с любым типом в кортеже, который можно сравнить с <.

Вот функция сортировки TupleLess, которая использует некоторый синтаксис стиля C:

 #include <string>
 #include <vector>
 #include <tuple>      

 template<int index> struct TupleLess
 {
    template<typename Tuple>
    bool operator() (const Tuple& left, const Tuple& right) const
    {
        return std::get<index>(left) < std::get<index>(right);
    }
};

int main(){
    //Define a Person tuple:    
    using Person = std::tuple<std::string, std::string, std::string>;

    //Create some Person tuples and add to a vector
    Person person1 = std::make_tuple("John", "Smith", "323 Fake Street");
    Person person2 = std::make_tuple("Sheila", "Henrod", "784 Unreal Avenue");
    Person person3 = std::make_tuple("Mitch", "LumpyLumps", "463 Falsified Lane");

    std::vector<Person> vPerson = std::vector<Person>();
    vPerson.push_back(person1);
    vPerson.push_back(person2);
    vPerson.push_back(person3);

    //Sort by index at position 2
    std::sort(vPerson.begin(), vPerson.end(), TupleLess<2>());
}