Пользовательская сортировка вектора кортежей
У меня есть вектор кортежей типа
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>());
}