How does one go about sorting a vector containing custom (i.e. user defined) objects.
Probably, standard STL algorithm sort along with a predicate (a function or a function object) which would operate on one of the fields (as a key for sorting) in the custom object should be used.
Am I on the right track?

- 11,239
- 22
- 63
- 66
-
1Possible duplicate of [Standard library sort and user defined types](https://stackoverflow.com/questions/1181246/standard-library-sort-and-user-defined-types) – MCCCS Jun 09 '18 at 13:18
15 Answers
A simple example using std::sort
struct MyStruct
{
int key;
std::string stringValue;
MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
};
struct less_than_key
{
inline bool operator() (const MyStruct& struct1, const MyStruct& struct2)
{
return (struct1.key < struct2.key);
}
};
std::vector < MyStruct > vec;
vec.push_back(MyStruct(4, "test"));
vec.push_back(MyStruct(3, "a"));
vec.push_back(MyStruct(2, "is"));
vec.push_back(MyStruct(1, "this"));
std::sort(vec.begin(), vec.end(), less_than_key());
Edit: As Kirill V. Lyadvinsky pointed out, instead of supplying a sort predicate, you can implement the operator<
for MyStruct
:
struct MyStruct
{
int key;
std::string stringValue;
MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
bool operator < (const MyStruct& str) const
{
return (key < str.key);
}
};
Using this method means you can simply sort the vector as follows:
std::sort(vec.begin(), vec.end());
Edit2: As Kappa suggests you can also sort the vector in the descending order by overloading a >
operator and changing call of sort a bit:
struct MyStruct
{
int key;
std::string stringValue;
MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
bool operator > (const MyStruct& str) const
{
return (key > str.key);
}
};
And you should call sort as:
std::sort(vec.begin(), vec.end(),greater<MyStruct>());
-
2Could you explain why you made the compare function in the struct less_than_key (in the first) example inline? – kluka May 15 '13 at 18:10
-
2and another question/note: if one would like to have multiple sorting methods (for different attributes) in a class the way of overloading the < operator is probably not an option, right? – kluka May 15 '13 at 18:28
-
8A cool thing is to provide also operator> method. This will allow us to sort in reverse order like: `std::sort(vec.begin(), vec.end(), greater
())`, which is clean and elegant. – kappa May 30 '14 at 23:21 -
1`std::sort(vec.begin(), vec.end(),greater
());` tells me that "greater" is undefined – Bovaz Aug 24 '15 at 21:29 -
5
-
-
1How does this need to be modified to sort a a vector of pointers to the struct instances? `vector
` – James Wierzba Jan 25 '16 at 15:52 -
6@kappa: Where you could just have `operator<` and use either `std::sort(vec.begin(), vec.end());` or `std::sort(vec.rbegin(), vec.rend());` depending on whether you want to have ascending or descending order. – Pixelchemist May 19 '16 at 22:24
-
I may be wrong, but I believe in the first bit of code, it should be `std::sort(vec.begin(), vec.end(), less_than_key);` not `std::sort(vec.begin(), vec.end(), less_than_key());`. – Jack Pan Jul 19 '16 at 14:40
-
@Alan, can you please explain what your `inline bool operator()` is? Is it something like an operator=? Can you point me to some references where I could read up on this? – gen Oct 26 '16 at 23:01
-
While running the sort function,if I do not add const in the comparator operator,it generates an error.Can someone tell why? – Varun Manocha Sep 23 '17 at 18:41
-
Can you have a constructor in a struct ? This doesn't seem to compile on MinGW – Damien Nov 05 '18 at 09:40
-
In the first way, `inline bool operator() (const MyStruct& struct1, const MyStruct& struct2)` - why are using `operator()` instead of `operator<` like usual operator overloading? Does using `()` implies `<` here? – shamiul97 Jul 14 '20 at 02:29
-
In the interest of coverage. I put forward an implementation using lambda expressions.
C++11
#include <vector>
#include <algorithm>
using namespace std;
vector< MyStruct > values;
sort( values.begin( ), values.end( ), [ ]( const MyStruct& lhs, const MyStruct& rhs )
{
return lhs.key < rhs.key;
});
C++14
#include <vector>
#include <algorithm>
using namespace std;
vector< MyStruct > values;
sort( values.begin( ), values.end( ), [ ]( const auto& lhs, const auto& rhs )
{
return lhs.key < rhs.key;
});

- 8,204
- 6
- 48
- 78
-
8To be clear, this results in ascending order; use `>` instead of `<` to get descending order. – bhaller Apr 27 '17 at 04:53
You could use functor as third argument of std::sort
, or you could define operator<
in your class.
struct X {
int x;
bool operator<( const X& val ) const {
return x < val.x;
}
};
struct Xgreater
{
bool operator()( const X& lx, const X& rx ) const {
return lx.x < rx.x;
}
};
int main () {
std::vector<X> my_vec;
// use X::operator< by default
std::sort( my_vec.begin(), my_vec.end() );
// use functor
std::sort( my_vec.begin(), my_vec.end(), Xgreater() );
}

- 97,037
- 24
- 136
- 212
-
5
-
7
-
1If that is the case then why we pass "const X& val", I assume that passing the value as const to a function makes the function think that its value is not going to be changed. – Prashant Bhanarkar Aug 22 '16 at 06:47
-
5@PrashantBhanarkar The `const` keyword at the end of the signature specifies that the `operator()` function does not change the instance of the `Xgreater` struct (which in general could have member variables), whereas indicating `const` for the input values only specifies that those input values are immutable. – schester Dec 28 '17 at 19:35
-
@PrashantBhanarkar const in optional you can use it if you want . But using it makes sorting safe as you are using & . – Brijesh Mar 27 '22 at 09:56
You are on the right track. std::sort
will use operator<
as comparison function by default. So in order to sort your objects, you will either have to overload bool operator<( const T&, const T& )
or provide a function object that does the comparison, much like this:
struct C {
int i;
static bool before( const C& c1, const C& c2 ) { return c1.i < c2.i; }
};
bool operator<( const C& c1, const C& c2 ) { return c1.i > c2.i; }
std::vector<C> values;
std::sort( values.begin(), values.end() ); // uses operator<
std::sort( values.begin(), values.end(), C::before );
The advantage of the usage of a function object is that you can use a function with access to the class' private members.

- 40,723
- 12
- 105
- 192
-
-
1It is better to make `operator<` a member of class (or struct), because global one could use protected or private members. Or you should make it a friend of struct C. – Kirill V. Lyadvinsky Sep 04 '09 at 17:25
Sorting such a vector
or any other applicable (mutable input iterator) range of custom objects of type X
can be achieved using various methods, especially including the use of standard library algorithms like
Since most of the techniques, to obtain relative ordering of X
elements, have already been posted, I'll start by some notes on "why" and "when" to use the various approaches.
The "best" approach will depend on different factors:
- Is sorting ranges of
X
objects a common or a rare task (will such ranges be sorted a mutiple different places in the program or by library users)? - Is the required sorting "natural" (expected) or are there multiple ways the type could be compared to itself?
- Is performance an issue or should sorting ranges of
X
objects be foolproof?
If sorting ranges of X
is a common task and the achieved sorting is to be expected (i.e. X
just wraps a single fundamental value) then on would probably go for overloading operator<
since it enables sorting without any fuzz (like correctly passing proper comparators) and repeatedly yields expected results.
If sorting is a common task or likely to be required in different contexts, but there are multiple criteria which can be used to sort X
objects, I'd go for Functors (overloaded operator()
functions of custom classes) or function pointers (i.e. one functor/function for lexical ordering and another one for natural ordering).
If sorting ranges of type X
is uncommon or unlikely in other contexts I tend to use lambdas instead of cluttering any namespace with more functions or types.
This is especially true if the sorting is not "clear" or "natural" in some way. You can easily get the logic behind the ordering when looking at a lambda that is applied in-place whereas operator<
is opague at first sight and you'd have to look the definition up to know what ordering logic will be applied.
Note however, that a single operator<
definition is a single point of failure whereas multiple lambas are multiple points of failure and require a more caution.
If the definition of operator<
isn't available where the sorting is done / the sort template is compiled, the compiler might be forced to make a function call when comparing objects, instead of inlining the ordering logic which might be a severe drawback (at least when link time optimization/code generation is not applied).
Ways to achieve comparability of class X
in order to use standard library sorting algorithms
Let std::vector<X> vec_X;
and std::vector<Y> vec_Y;
1. Overload T::operator<(T)
or operator<(T, T)
and use standard library templates that do not expect a comparison function.
Either overload member operator<
:
struct X {
int i{};
bool operator<(X const &r) const { return i < r.i; }
};
// ...
std::sort(vec_X.begin(), vec_X.end());
or free operator<
:
struct Y {
int j{};
};
bool operator<(Y const &l, Y const &r) { return l.j < r.j; }
// ...
std::sort(vec_Y.begin(), vec_Y.end());
2. Use a function pointer with a custom comparison function as sorting function parameter.
struct X {
int i{};
};
bool X_less(X const &l, X const &r) { return l.i < r.i; }
// ...
std::sort(vec_X.begin(), vec_X.end(), &X_less);
3. Create a bool operator()(T, T)
overload for a custom type which can be passed as comparison functor.
struct X {
int i{};
int j{};
};
struct less_X_i
{
bool operator()(X const &l, X const &r) const { return l.i < r.i; }
};
struct less_X_j
{
bool operator()(X const &l, X const &r) const { return l.j < r.j; }
};
// sort by i
std::sort(vec_X.begin(), vec_X.end(), less_X_i{});
// or sort by j
std::sort(vec_X.begin(), vec_X.end(), less_X_j{});
Those function object definitions can be written a little more generic using C++11 and templates:
struct less_i
{
template<class T, class U>
bool operator()(T&& l, U&& r) const { return std::forward<T>(l).i < std::forward<U>(r).i; }
};
which can be used to sort any type with member i
supporting <
.
4. Pass an anonymus closure (lambda) as comparison parameter to the sorting functions.
struct X {
int i{}, j{};
};
std::sort(vec_X.begin(), vec_X.end(), [](X const &l, X const &r) { return l.i < r.i; });
Where C++14 enables a even more generic lambda expression:
std::sort(a.begin(), a.end(), [](auto && l, auto && r) { return l.i < r.i; });
which could be wrapped in a macro
#define COMPARATOR(code) [](auto && l, auto && r) -> bool { return code ; }
making ordinary comparator creation quite smooth:
// sort by i
std::sort(v.begin(), v.end(), COMPARATOR(l.i < r.i));
// sort by j
std::sort(v.begin(), v.end(), COMPARATOR(l.j < r.j));

- 24,090
- 7
- 47
- 71
-
In *2.* case you wrote `bool X_less(X const &l, X const &r) const { return l.i < r.i; }` for comparator but the `const` keywords should be removed (as it's not a member function). – PolGraphic Aug 29 '16 at 17:43
-
-
@Pixelchemist how would I use the (4.) lambda approach when not using `std::sort` or similar, but needed an instance of `Compare`, e.g. when instantiating a `std::set` ? – azrdev Mar 28 '18 at 13:56
-
1@azrdev: A function template that capture the type of the closure to pass it as a template parameter to set: `template
std::set – Pixelchemist Mar 29 '18 at 19:19make_set(C const& compare) { return std::set { compare }; }` which could be used like `auto xset = make_set ([](auto && l, auto && r) { return l.i < r.i; });`.
Below is the code using lambdas
#include <vector>
#include <algorithm>
using namespace std;
struct MyStruct
{
int key;
std::string stringValue;
MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}
};
int main()
{
std::vector < MyStruct > vec;
vec.push_back(MyStruct(4, "test"));
vec.push_back(MyStruct(3, "a"));
vec.push_back(MyStruct(2, "is"));
vec.push_back(MyStruct(1, "this"));
std::sort(vec.begin(), vec.end(),
[] (const MyStruct& struct1, const MyStruct& struct2)
{
return (struct1.key < struct2.key);
}
);
return 0;
}

- 598
- 7
- 15

- 197
- 1
- 3
- 7
I was curious if there is any measurable impact on performance between the various ways one can call std::sort, so I've created this simple test:
$ cat sort.cpp
#include<algorithm>
#include<iostream>
#include<vector>
#include<chrono>
#define COMPILER_BARRIER() asm volatile("" ::: "memory");
typedef unsigned long int ulint;
using namespace std;
struct S {
int x;
int y;
};
#define BODY { return s1.x*s2.y < s2.x*s1.y; }
bool operator<( const S& s1, const S& s2 ) BODY
bool Sgreater_func( const S& s1, const S& s2 ) BODY
struct Sgreater {
bool operator()( const S& s1, const S& s2 ) const BODY
};
void sort_by_operator(vector<S> & v){
sort(v.begin(), v.end());
}
void sort_by_lambda(vector<S> & v){
sort(v.begin(), v.end(), []( const S& s1, const S& s2 ) BODY );
}
void sort_by_functor(vector<S> &v){
sort(v.begin(), v.end(), Sgreater());
}
void sort_by_function(vector<S> &v){
sort(v.begin(), v.end(), &Sgreater_func);
}
const int N = 10000000;
vector<S> random_vector;
ulint run(void foo(vector<S> &v)){
vector<S> tmp(random_vector);
foo(tmp);
ulint checksum = 0;
for(int i=0;i<tmp.size();++i){
checksum += i *tmp[i].x ^ tmp[i].y;
}
return checksum;
}
void measure(void foo(vector<S> & v)){
ulint check_sum = 0;
// warm up
const int WARMUP_ROUNDS = 3;
const int TEST_ROUNDS = 10;
for(int t=WARMUP_ROUNDS;t--;){
COMPILER_BARRIER();
check_sum += run(foo);
COMPILER_BARRIER();
}
for(int t=TEST_ROUNDS;t--;){
COMPILER_BARRIER();
auto start = std::chrono::high_resolution_clock::now();
COMPILER_BARRIER();
check_sum += run(foo);
COMPILER_BARRIER();
auto end = std::chrono::high_resolution_clock::now();
COMPILER_BARRIER();
auto duration_ns = std::chrono::duration_cast<std::chrono::duration<double>>(end - start).count();
cout << "Took " << duration_ns << "s to complete round" << endl;
}
cout << "Checksum: " << check_sum << endl;
}
#define M(x) \
cout << "Measure " #x " on " << N << " items:" << endl;\
measure(x);
int main(){
random_vector.reserve(N);
for(int i=0;i<N;++i){
random_vector.push_back(S{rand(), rand()});
}
M(sort_by_operator);
M(sort_by_lambda);
M(sort_by_functor);
M(sort_by_function);
return 0;
}
What it does is it creates a random vector, and then measures how much time is required to copy it and sort the copy of it (and compute some checksum to avoid too vigorous dead code elimination).
I was compiling with g++ (GCC) 7.2.1 20170829 (Red Hat 7.2.1-1)
$ g++ -O2 -o sort sort.cpp && ./sort
Here are results:
Measure sort_by_operator on 10000000 items:
Took 0.994285s to complete round
Took 0.990162s to complete round
Took 0.992103s to complete round
Took 0.989638s to complete round
Took 0.98105s to complete round
Took 0.991913s to complete round
Took 0.992176s to complete round
Took 0.981706s to complete round
Took 0.99021s to complete round
Took 0.988841s to complete round
Checksum: 18446656212269526361
Measure sort_by_lambda on 10000000 items:
Took 0.974274s to complete round
Took 0.97298s to complete round
Took 0.964506s to complete round
Took 0.96899s to complete round
Took 0.965773s to complete round
Took 0.96457s to complete round
Took 0.974286s to complete round
Took 0.975524s to complete round
Took 0.966238s to complete round
Took 0.964676s to complete round
Checksum: 18446656212269526361
Measure sort_by_functor on 10000000 items:
Took 0.964359s to complete round
Took 0.979619s to complete round
Took 0.974027s to complete round
Took 0.964671s to complete round
Took 0.964764s to complete round
Took 0.966491s to complete round
Took 0.964706s to complete round
Took 0.965115s to complete round
Took 0.964352s to complete round
Took 0.968954s to complete round
Checksum: 18446656212269526361
Measure sort_by_function on 10000000 items:
Took 1.29942s to complete round
Took 1.3029s to complete round
Took 1.29931s to complete round
Took 1.29946s to complete round
Took 1.29837s to complete round
Took 1.30132s to complete round
Took 1.3023s to complete round
Took 1.30997s to complete round
Took 1.30819s to complete round
Took 1.3003s to complete round
Checksum: 18446656212269526361
Looks like all the options except for passing function pointer are very similar, and passing a function pointer causes +30% penalty.
It also looks like the operator< version is ~1% slower (I repeated the test multiple times and the effect persists), which is a bit strange as it suggests that the generated code is different (I lack skill to analyze --save-temps output).

- 460
- 4
- 9

- 5,374
- 2
- 35
- 44
In your class, you may overload the "<" operator.
class MyClass
{
bool operator <(const MyClass& rhs)
{
return this->key < rhs.key;
}
}

- 8,204
- 6
- 48
- 78

- 28,337
- 7
- 52
- 74
Yes, std::sort()
with third parameter (function or object) would be easier. An example:
http://www.cplusplus.com/reference/algorithm/sort/

- 21,608
- 12
- 74
- 82

- 4,785
- 1
- 23
- 17
-
Not exactly a link only answer but at least a single line example would be useful. – Manos Nikolaidis Dec 31 '15 at 11:20
In C++20 one can default operator<=> without a user-defined comparator. The compiler will take care of that.
#include <iostream>
#include <compare>
#include <vector>
#include <algorithm>
struct MyInt
{
int value;
MyInt(int val) : value(val) {}
auto operator<=>(const MyInt& other) const = default;
};
int main()
{
MyInt Five(5);
MyInt Two(2);
MyInt Six(6);
std::vector V{Five, Two, Six};
std::sort(V.begin(), V.end());
for (const auto& element : V)
std::cout << element.value << std::endl;
}
Output:
2
5
6

- 14,154
- 10
- 50
- 86
// sort algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::sort
#include <vector> // std::vector
using namespace std;
int main () {
char myints[] = {'F','C','E','G','A','H','B','D'};
vector<char> myvector (myints, myints+8); // 32 71 12 45 26 80 53 33
// using default comparison (operator <):
sort (myvector.begin(), myvector.end()); //(12 32 45 71)26 80 53 33
// print out content:
cout << "myvector contains:";
for (int i=0; i!=8; i++)
cout << ' ' <<myvector[i];
cout << '\n';
system("PAUSE");
return 0;
}

- 21
- 2
You can use user defined comparator class.
class comparator
{
int x;
bool operator()( const comparator &m, const comparator &n )
{
return m.x<n.x;
}
}
typedef struct Freqamp{
double freq;
double amp;
}FREQAMP;
bool struct_cmp_by_freq(FREQAMP a, FREQAMP b)
{
return a.freq < b.freq;
}
main()
{
vector <FREQAMP> temp;
FREQAMP freqAMP;
freqAMP.freq = 330;
freqAMP.amp = 117.56;
temp.push_back(freqAMP);
freqAMP.freq = 450;
freqAMP.amp = 99.56;
temp.push_back(freqAMP);
freqAMP.freq = 110;
freqAMP.amp = 106.56;
temp.push_back(freqAMP);
sort(temp.begin(),temp.end(), struct_cmp_by_freq);
}
if compare is false, it will do "swap".

- 1,286
- 11
- 14
To sort a vector you can use the sort() algorithm in .
sort(vec.begin(),vec.end(),less<int>());
The third parameter used can be greater or less or any function or object can also be used. However the default operator is < if you leave third parameter empty.
// using function as comp
std::sort (myvector.begin()+4, myvector.end(), myfunction);
bool myfunction (int i,int j) { return (i<j); }
// using object as comp
std::sort (myvector.begin(), myvector.end(), myobject);

- 456
- 8
- 20
Since this is the top Google result for this question I'll post my preferred way (similar to many of the above, but the finer points are different). Also this is a copy/paste runnable example:
// SortObjectByField.cpp
#include <iostream>
#include <vector>
#include <algorithm>
struct Meeting
{
int startTime;
int endTime;
Meeting() { }
Meeting(int startTime, int endTime)
{
this->startTime = startTime;
this->endTime = endTime;
}
struct SortByStartTime
{
bool operator()(const Meeting& meeting1, const Meeting& meeting2)
{
return meeting1.startTime < meeting2.startTime;
}
};
struct SortByEndTime
{
bool operator()(const Meeting& meeting1, const Meeting& meeting2)
{
return meeting1.endTime < meeting2.endTime;
}
};
};
// function prototypes
void printMeetings(const std::vector<Meeting>& meetings);
int main(void)
{
std::vector<Meeting> meetings = { Meeting(5, 10), Meeting(15, 20), Meeting(2, 25) };
std::sort(meetings.begin(), meetings.end(), Meeting::SortByStartTime());
printMeetings(meetings);
std::sort(meetings.begin(), meetings.end(), Meeting::SortByEndTime());
printMeetings(meetings);
return 0;
}
void printMeetings(const std::vector<Meeting>& meetings)
{
std::cout << "\n";
for (auto& meeting : meetings)
{
std::cout << "( " << meeting.startTime << ", " << meeting.endTime << " )" << "\n";
}
std::cout << "\n";
}

- 3,402
- 10
- 49
- 75