Sort vector C++ tasks usually start with the same deceptively simple snippet. For a plain std::vector<int>, the shortest working solution is three lines:
#include <algorithm>
#include <vector>
std::vector<int> v = {42, 7, 19, 3, 88, 1};
std::sort(v.begin(), v.end());
That covers ascending order, and only ascending order. Once you need descending order, custom structs, pairs, or 2D vectors, the easy fix stops working until you make a small design decision about the comparator. And if your search actually leads you to C rather than C++, the situation changes completely: there is no std::vector and the standard tool is a C function called qsort. I’ll cover both worlds here, starting with the C++ standard library.
Sort Vector C++ with std::sort: Default Ascending Order
Minimal example: vector from unsorted to sorted
Here is a complete, self-contained example you can paste into a file and compile immediately:
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {42, 7, 19, 3, 88, 1};
std::sort(v.begin(), v.end());
for (int n : v) {
std::cout << n << ' ';
}
std::cout << '\n';
}
Output:
1 3 7 19 42 88
The two arguments, v.begin() and v.end(), define the half-open range. Sorting applies to every element from the first iterator up to, but not including, the second one. If you ever hear someone refer to “begin() and end() iterators,” this is the pattern they mean.
What std::sort uses by default
The default comparison behavior relies on operator< before C++20. From C++20 onward, the standard describes the default comparator as std::less{}, which in practice produces the same result for the vast majority of types: element a is ordered before element b whenever a < b.
There are two constraints worth remembering:
std::sortrequires random access iterators.std::vectorprovides them, but containers likestd::listdo not. If you try to sort a list withstd::sort, it will not compile.std::sortdoes not guarantee that equivalent elements keep their original relative order. If you have records with equal keys and need stability,std::stable_sortis the safer choice.
C++ Sort Vector Descending Order: Three Working Methods
Every few months I see a pull request where someone sorts ascending and then calls std::reverse to “fix” it. That works, but it is an unnecessary second pass. You have cleaner options.
Method 1: std::greater<>() comparator
The quickest documented solution uses the function object std::greater, which wraps operator>:
#include <algorithm>
#include <functional>
#include <vector>
std::vector<int> v = {42, 7, 19, 3, 88, 1};
std::sort(v.begin(), v.end(), std::greater<int>());
The result is 88 42 19 7 3 1. In C++14 and later, you can also write std::greater<>() and let the compiler deduce the type.
Method 2: lambda comparator
Lambdas are my default for any non-trivial ordering rule. The comparator receives two elements and returns true when the first one must come before the second:
std::sort(v.begin(), v.end(),
[](int a, int b) {
return a > b;
});
The important mental model is this: the lambda should express “a comes before b,” not “a is greater than b in some abstract sense.” If you write a comparator that returns true for equal elements, you have broken strict weak ordering, and the behavior of std::sort becomes undefined. That is not a warning you want to debug at 2 AM.
Method 3: reverse iterators rbegin() and rend()
You can also sort descending without writing any comparator at all:
std::sort(v.rbegin(), v.rend());
Why does that work? Reverse iterators make the algorithm see the vector backward, so what the sort routine treats as “ascending” becomes descending from the original vector’s point of view. This one-liner is concise, but I find it less readable when the ordering rule is not obvious to the next developer. Time complexity remains the same as a normal sort: O(N log N).
Sort Vector of Objects in C++: Structs and Custom Comparators
The moment your vector contains objects, std::sort needs more guidance. A Person struct has no built-in meaning of “less than,” and the compiler will not guess whether sorting by age, name, or salary is intended.
Lambda comparator for a member field
Suppose you have a simple struct:
#include <algorithm>
#include <string>
#include <vector>
struct Person {
std::string name;
int age;
};
To sort a std::vector<Person> by age, pass a lambda that compares the field:
std::vector<Person> people = {
{"Alice", 34},
{"Bob", 29},
{"Charlie", 41}
};
std::sort(people.begin(), people.end(),
[](const Person& a, const Person& b) {
return a.age < b.age;
});
The result is Bob (29), Alice (34), Charlie (41). Notice the parameters are const Person&. In code reviews, the most common mistake I see is taking parameters by value, which copies whole objects on every comparison. For a struct with a std::string field, that overhead adds up quickly on a large vector.
Overload operator< for natural ordering
If a type has one obvious natural ordering, you can make the default two-argument sort work by overloading operator<:
struct Person {
std::string name;
int age;
bool operator<(const Person& other) const {
return age < other.age;
}
};
After that, std::sort(people.begin(), people.end()) compiles and sorts by age. My advice is to use this sparingly. When a domain object has multiple plausible orderings — creation date, priority, display name — overloading operator< forces one choice on every future consumer of the type. A named comparator or lambda documents the intent at the call site.
Sorting by multiple fields
For multi-level ordering, chain the comparisons in one lambda:
std::sort(people.begin(), people.end(),
[](const Person& a, const Person& b) {
if (a.age != b.age) {
return a.age < b.age;
}
return a.name < b.name;
});
This expression preserves strict weak ordering because every fallback comparison is consistent. A more compact alternative uses std::tie, but the explicit if version is easier to read and less likely to confuse beginners.
return std::tie(a.age, a.name) < std::tie(b.age, b.name);
Both approaches sort primarily by age and alphabetically by name inside each age group.
Sort Vector of Pairs, Strings, and 2D Vectors in C++
Default pair sorting: first element then second
std::pair already defines lexicographic comparison, so sorting a std::vector<std::pair<int, std::string>> requires no custom comparator:
#include <utility>
#include <vector>
std::vector<std::pair<int, std::string>> items = {
{3, "pear"},
{1, "apple"},
{2, "cherry"},
{1, "avocado"}
};
std::sort(items.begin(), items.end());
After the sort, the order is (1, "apple"), (1, "avocado"), (2, "cherry"), (3, "pear"). The pairs are ordered by first, then by second. This default covers many key-value use cases immediately.
Sort vector of pairs by second element
When the natural ordering does not match your business logic, reach for a lambda again:
std::sort(items.begin(), items.end(),
[](const auto& a, const auto& b) {
return a.second < b.second;
});
This example uses a generic lambda, which requires C++14. If your project still compiles with C++11, write the parameters explicitly as const std::pair<int, std::string>&. If you only need sorting by first, compare a.first and b.first; note that duplicate keys are not guaranteed to keep their original order under std::sort.
Strings and 2D vectors follow the same rules
A std::vector<std::string> sorts alphabetically by default because std::string defines operator<:
std::vector<std::string> words = {"pear", "Apple", "orange", "banana"};
std::sort(words.begin(), words.end());
// Output: Apple banana orange pear
This is case-sensitive lexicographic ordering. Capital letters sort before lowercase because of their ASCII values. If you need case-insensitive sorting, provide a comparator that calls a case-folding function on each argument.
For a 2D vector, define what “sorting” means first. Sorting every inner row is one operation:
std::vector<std::vector<int>> matrix = {{3, 1}, {2, 4}, {0, 5}};
for (auto& row : matrix) {
std::sort(row.begin(), row.end());
}
// matrix is now {{1, 3}, {2, 4}, {0, 5}}
To sort the outer vector by the first element of each row, you reorder entire rows:
std::sort(matrix.begin(), matrix.end(),
[](const std::vector<int>& a, const std::vector<int>& b) {
return a[0] < b[0];
});
// matrix is now {{0, 5}, {1, 3}, {2, 4}}
Both operations are common in competitive programming and data preprocessing, and both reuse the same std::sort rules.
Best Way to Sort Vector in C++: std::sort vs. stable_sort vs. partial_sort
There is no universal “best” sorting function — only the right tool for a given constraint. Here is how the main candidates compare.
Comparison table
| Algorithm | Header | Stable? | Time complexity | Typical scenario |
|---|---|---|---|---|
std::sort | <algorithm> | No | O(N log N) worst case | Default general-purpose sorting |
std::stable_sort | <algorithm> | Yes | O(N log N) if extra memory is available; otherwise O(N log² N) [需核实] | Preserving the original order of equal elements |
std::partial_sort | <algorithm> | No | O(N log M) | When only the top M smallest elements matter |
qsort | <cstdlib> | Not guaranteed | Not specified by ISO C; typical implementations are O(N log N) | Pure C code |
The worst-case guarantee for std::sort is a meaningful detail. The C++ standard, through defect report LWG 713, now requires O(N log N) even in the worst case, not merely on average. |
Decision rules for IT professionals
- Use
std::sortas your default. It is fast, widely optimized, and sufficient in probably 90% of production code. - Choose
std::stable_sortwhen equal elements must preserve their original order — for example, when sorting a report by department name and you want people within each department to stay in their original input order. - Choose
std::partial_sortwhen you need the smallest N elements of a huge collection and do not care about the rest. It is more efficient than sorting the entire vector and then truncating. - Use
qsortonly in C code or when you are forced to pass a C-style callback into an existing C API.
If you want to impress reviewers, mention that std::sort requires move-constructible and move-assignable element types. That requirement is part of the standard, and it fails for types with const members.
How Does std::sort Work? Complexity and Introsort Explained
Why std::sort is introsort, not plain quicksort
Beginners often hear “quicksort” and assume std::sort is pure quicksort. It usually is not. Modern implementations, including libstdc++ and libc++, use a hybrid algorithm called introsort.
Introsort begins with quicksort, switches to heapsort when the recursion depth becomes too deep, and uses insertion sort for small partitions. Quicksort alone can degrade to O(N²) on adversarial inputs; introsort was designed specifically to prevent that. Because of this hybrid design, the implementation keeps its average-case speed while guaranteeing that the worst case stays close to O(N log N) comparisons.
What O(N log N) means in practice
For a vector of N elements, std::sort performs roughly N log₂N comparisons. That formula is not the whole story. The other cost is moving or swapping elements. When each element is an int, moves are nearly free. When each element is a large object with several std::string members, moving those strings — and possibly allocating memory during a comparison-heavy sort — dominates runtime.
The practical lesson I keep coming back to: the comparator’s cost matters, but element movement often matters more. If you cannot avoid sorting expensive-to-copy objects, consider sorting a vector of lightweight keys or indices instead.
How to Sort a Vector in C++ Without std::sort: A Merge Sort Example
When hand-writing a sort makes sense
In production code, hand-written sorting is usually the wrong answer. std::sort is battle-tested, optimized for your hardware, and far less likely to contain an off-by-one error. There are two legitimate reasons to write your own: algorithm exercises and coding interviews. I have asked more than one candidate to implement merge sort, and I still find it a useful signal for how someone reasons about recursion and memory.
Merge sort implementation pattern
A classic top-down merge sort splits the vector in half, sorts each half, and merges the results:
#include <iostream>
#include <vector>
void merge(std::vector<int>& arr, int left, int mid, int right) {
std::vector<int> temp;
temp.reserve(right - left);
int i = left;
int j = mid;
while (i < mid && j < right) {
if (arr[i] <= arr[j]) {
temp.push_back(arr[i++]);
} else {
temp.push_back(arr[j++]);
}
}
while (i < mid) temp.push_back(arr[i++]);
while (j < right) temp.push_back(arr[j++]);
for (int k = 0; k < (int)temp.size(); ++k) {
arr[left + k] = temp[k];
}
}
void mergeSort(std::vector<int>& arr, int left, int right) {
if (right - left <= 1) return;
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid, right);
merge(arr, left, mid, right);
}
Call it like this:
std::vector<int> v = {38, 27, 43, 3, 9, 82, 10};
mergeSort(v, 0, static_cast<int>(v.size()));
A correctly implemented merge sort is stable and runs in O(N log N), but it requires extra memory. The version above also performs many temporary vector allocations across recursive calls; you could optimize that by passing a reusable buffer if performance ever mattered.
Is There a Vector in C? Sorting C Arrays with qsort()
qsort example for descending int array
No, C has no standard vector type. The closest native data structure is a plain array, and the standard sorting tool is qsort, declared in <stdlib.h>. The function takes a comparator whose signature looks intimidating at first:
#include <stdio.h>
#include <stdlib.h>
int cmp_desc(const void* a, const void* b) {
int ia = *(const int*)a;
int ib = *(const int*)b;
return (ia < ib) - (ia > ib);
}
int main(void) {
int arr[] = {42, 7, 19, 3, 88, 1};
size_t n = sizeof(arr) / sizeof(arr[0]);
qsort(arr, n, sizeof(int), cmp_desc);
for (size_t i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
Output:
88 42 19 7 3 1
The comparator must return negative, zero, or positive depending on whether the first value is ordered before, equal to, or after the second. The common idiom return *(int*)b - *(int*)a; works for many inputs, but it risks signed integer overflow when comparing extreme values. The explicit comparison version above avoids that entirely.
When to use qsort in a C++ project
If the project is pure C, qsort is the standard tool. In a C++ project, I see no strong reason to prefer it. qsort is not type-safe — it shuffles bytes through a void* interface — and calling it on a std::vector requires passing vec.data() with the correct element size. That can go wrong for any type with non-trivial copy semantics.
If you are writing C++, use std::vector and std::sort. If you are maintaining a legacy C codebase, keep using qsort on arrays. The two coexist more peacefully when you keep the language boundaries clear.
Frequently Asked Questions
How do I sort a vector in C++?
The shortest copy-paste answer is:
#include <algorithm>
#include <vector>
std::vector<int> v = {5, 2, 8, 1};
std::sort(v.begin(), v.end());
That sorts the vector ascending using operator<. For descending order, use std::greater<int>(), a lambda, or reverse iterators.
How do I sort a vector of custom objects or structs in C++?
std::sort needs either an overloaded operator< for your struct or an explicit comparator. The most actionable pattern is a lambda over a member field:
std::sort(people.begin(), people.end(),
[](const Person& a, const Person& b) {
return a.age < b.age;
});
The comparator must express “a comes before b” and must not modify the objects.
What is the difference between std::sort and std::stable_sort?
Both sort a range, but std::sort is not required to preserve the original order of equivalent elements. std::stable_sort guarantees that equal elements stay in their original relative order. That guarantee comes with additional memory usage and often slower performance, so use stable_sort only when stability genuinely matters.
Is there a vector in C?
No. C has no standard generic vector type. The native equivalent is a plain array, and the matching sorting function is qsort from <stdlib.h>. A minimal descending-order example is the qsort code shown above.
Why is my vector not sorting properly in C++?
Check these three causes in order:
- Missing
#include <algorithm>— older compilers may give strange errors aboutsortbeing undeclared. - A comparator that violates strict weak ordering, such as
return a >= b;. Equal elements must never make the comparator returntruefor both directions. - Accidentally sorting a copy of the vector while printing the original. Call
std::sorton the original vector’s iterators, not on a temporary copy.
When in doubt, build the smallest reproducer and run it in an online compiler before blaming the sort itself.
Key Takeaways: The Short Version
The default recipe for any sort vector C++ task is short and reliable: std::sort(vec.begin(), vec.end());. Everything else is a variation on the comparator. For descending order, use std::greater, a lambda, or reverse iterators. For custom objects, pairs, and nested vectors, write a clean comparator that respects strict weak ordering. If you need stability, switch to std::stable_sort. If you are writing C, use qsort on an array and stop expecting a std::vector to exist.
Bookmark this guide and copy the minimal snippet into your next project. When you hit a comparator problem, open Compiler Explorer and test the smallest reproducer first — sorting bugs are far easier to fix in isolation than inside a large codebase.





