In a brokerage's HTS (Home Trading System) and MTS server environment, where tens of thousands of trade executions and order records pour in every second, C++ remains the core language of choice for its overwhelming performance and fine-grained memory control.
But when you focus too heavily on performance alone, it's easy to lose sight of readability and maintainability. Where older C++ (C++03 and earlier) manually managed memory and iterators in a "way that was easy for the machine to understand," Modern C++ from C++11 onward focuses on "clearly expressing the developer's 'intent' in the code to prevent human error."
Today I'll walk through a scenario that comes up often in HTS work — "looking up and iterating over multiple order records for a specific stock (`std::multimap`)" — to show how legacy C++ code can be refactored into a modern C++ style.
1. Real-World Scenario: Retrieving All Orders for a Specific Stock
- We have a
std::multimapdata structure keyed by stock code (e.g.,"005930"for Samsung Electronics), with order information (anOrderobject) as the value. - We need to search for every order that came in for a given stock code, mark them as executed, and display them on screen.
2. Limitations of the Legacy C++03-Style Code
In the old C++03 style, developers would combine lower_bound and upper_bound, often spelling out long, complicated iterator types by hand.
// [Legacy C++03-style code]
#include <iostream>
#include <map>
#include <string>
struct Order {
long long orderId;
int price;
int qty;
};
typedef std::multimap<std::string, Order> OrderMap;
void ProcessOrders(const OrderMap& mapOrders, const std::string& stockCode) {
// 1. Spelling out the long, complex iterator type
OrderMap::const_iterator itLower = mapOrders.lower_bound(stockCode);
OrderMap::const_iterator itUpper = mapOrders.upper_bound(stockCode);
// 2. Iterating while manually comparing the range
for (OrderMap::const_iterator it = itLower; it != itUpper; ++it) {
std::cout << "Stock: " << it->first
<< " | Order ID: " << it->second.orderId
<< " | Price: " << it->second.price << "\n";
}
}
- Visual noise (type noise): the repeated, verbose
OrderMap::const_iteratordeclarations bury the actual order-processing logic that matters. - Unclear intent: calling
lower_boundandupper_boundseparately lists out the individual steps for computing the range (the "how"), rather than expressing the developer's actual intent — "I want the entire range for this key." - Room for human error: across two separate search calls, it's easy to pass the wrong key by mistake, or to accidentally compare against the wrong iterator instead of
itUpperin the loop condition.
3. Step-by-Step Modern C++ Refactoring
Step 1. Removing Type Noise with the auto Keyword (C++11)
Introducing C++11's auto lets the compiler infer the type, so the verbose const_iterator syntax disappears and the code becomes much more concise.
// [Step 1: introducing auto]
void ProcessOrders_Step1(const OrderMap& mapOrders, const std::string& stockCode) {
auto itLower = mapOrders.lower_bound(stockCode);
auto itUpper = mapOrders.upper_bound(stockCode);
for (auto it = itLower; it != itUpper; ++it) {
std::cout << "Order ID: " << it->second.orderId << "\n";
}
}
Step 2. Clarifying the Developer's "Intent" with std::equal_range (C++11)
Instead of calling lower_bound and upper_bound separately, using std::equal_range (or multimap::equal_range) returns the start and end iterator pair (std::pair) for a given key in a single call.
It conveys the business intent — "extract the entire range of elements matching this key at once" — in a single, clear word.
// [Step 2: using equal_range]
void ProcessOrders_Step2(const OrderMap& mapOrders, const std::string& stockCode) {
// Returns the [itBegin, itEnd] pair in one call
auto range = mapOrders.equal_range(stockCode);
for (auto it = range.first; it != range.second; ++it) {
std::cout << "Order ID: " << it->second.orderId << "\n";
}
}
Step 3. Finishing the Job with C++17 Structured Bindings
With C++17's structured bindings, instead of opaque member names like range.first and range.second, you can give them self-explanatory names like [rangeBegin, rangeEnd].
// [Step 3: applying C++17 structured bindings - the final version]
void ProcessOrders_Final(const OrderMap& mapOrders, const std::string& stockCode) {
// Intuitively destructured: range.first -> rangeBegin, range.second -> rangeEnd
auto [rangeBegin, rangeEnd] = mapOrders.equal_range(stockCode);
for (auto it = rangeBegin; it != rangeEnd; ++it) {
const auto& [code, order] = *it; // The element itself can also be destructured into key and value
std::cout << "Stock: " << code
<< " | Order ID: " << order.orderId
<< " | Quantity: " << order.qty << "\n";
}
}
4. Before/After Comparison and Value Analysis
- Improved readability: the unnecessary boilerplate of the
OrderMap::const_iteratorstyle is drastically reduced. - Clearer intent: using
equal_rangemakes the meaning — "fetch this entire range" — directly visible in the code. - Improved safety: removes room for small human errors like off-by-one iterator boundaries or key typos.
5. Wrapping Up
Good code isn't just "code the compiler interprets well" — it's "code that fellow developers, and your future self, can read quickly and understand the intent behind."
If you're working with legacy C++ code in an existing project, try replacing unnecessary iterator type declarations with auto, equal_range, and structured bindings. Even a small refactoring like this can reduce bugs and significantly raise the quality of your code.