Logo Xingxin on Bug

The Pitfall of Using C++ std::min and std::min_element

November 30, 2024
3 min read
Warning

The std::min is very different from std::min_element!!

☕The Story

It was a typical day at the office, and I was working on optimizing some code. The task seems easy: find the minimum item in a std::vector container. Confidently, I typed out what I thought was the correct line of code😎:

const auto numbers = std::vector<int>{9, 0, 2, 3, 6, 8, 7, 1};
const auto min = std::min(numbers.cbegin(), numbers.cend());

Great, the program is compiled, and I thought I had nailed it😏 and the result is trivially to be 0. However, as I started running the application, the results were far from what I expected. Instead of getting the smallest element in the vector, the program was producing bizarre outcomes and it was 9😱.

Perplexed, I dug deeper and soon realized my mistake. I had confused std::min with std::min_element. Although they look similar by their names, their functionalities are quite different. This small oversight led to unexpected behavior in my code.

🤔Understanding the Difference

The difference is trivial.

  • std::min is used to find the smaller of 2 values.
  • std::min_element is used to find the smallest element in a range.

Remark

In math, the std::min is a binary operation while the input of std::min_element is a group of something.
diff-stdmin-std-minelement-excalidraw.svg

🚭The Mistake

When I used std::min(numbers.cbegin(), numbers.cend()), the compiler interpreted it as comparing the two iterators numbers.cbegin() and numbers.cend()🫢. Therefore, I am actually comparing the minimum value of 2 memory address of pointers!

compare-2-pointer-address-excalidraw.svg

✏Can we do better?

From the interface of std::min and std::min_element, I see a similarity

template< class T, class Compare >
const T& std::min( const T& a, const T& b, Compare comp );
 
template< class ForwardIt > 
ForwardIt min_element( ForwardIt first, ForwardIt last );

They both receive 2 arguments… That’s my “root cause” of this bug.

📌solution 1 Use std::ranges::min_element in C++20 . In this way, you will not accidently hit into the binary operation.

const auto min_element = std::ranges::min_element(numbers);

📌solution 2 Explicitly fill the template parameter T:

//std::min<int>(numbers.begin(), numbers.end())❌ failed to compile

And we are aware we made some mistake from this compiler error message.