Free tools Windows power users keep installed
One-click scans. No signup required.
A C++ function returns one expression, but that expression can be an object containing several results. For most APIs, return a small named struct; use std::pair or std::tuple for compact, fixed groups, then unpack them with structured bindings in C++17 or later. Output-reference parameters remain useful when caller-owned storage or a legacy interface is part of the design.
Return one object that contains the results
C++ does not give a function multiple independent return statements at once. Instead, the function returns one object, and that object can hold multiple values. Microsoft Learn describes several ways to do this, including a named class or struct, std::pair, std::tuple, and pass-by-reference output parameters (Microsoft Learn: Returning Values).
For example, integer division produces a quotient and a remainder. Both can be represented by one result object:
struct DivisionResult {
int quotient;
int remainder;
};
DivisionResult divide(int dividend, int divisor) {
return {dividend / divisor, dividend % divisor};
}
auto result = divide(17, 5);
// result.quotient == 3
// result.remainder == 2
The function returns a single DivisionResult. Its named members make each value’s purpose clear wherever the result is used.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minute#1 Best Overall
Choose the result type that fits the relationship
Use a named struct for meaningful or evolving results
A named type is usually the clearest choice when the values have distinct meanings, the result is part of a public API, or the set of results may grow. Callers write result.quotient rather than relying on a positional index such as std::get<0>(result). Adding another named member can also make an expanded contract easier to understand.
Use std::pair for exactly two related values
std::pair, declared in <utility>, holds two values. It suits results that naturally come as a pair, such as an iterator and an insertion flag, or a quotient and remainder:
#include <utility>
std::pair<int, int> divide_pair(int dividend, int divisor) {
return {dividend / divisor, dividend % divisor};
}
Without structured bindings, access the values as first and second. Those names are generic, so a pair is less self-documenting than a domain-specific struct when the values’ roles are not immediately obvious. The standard library defines std::pair as a two-element container (cppreference: std::pair).
Use std::tuple for a fixed group of values
std::tuple, declared in <tuple>, can hold a fixed number of values with different types:
#include <string>
#include <tuple>
std::tuple<int, std::string, double> read_record() {
return {108, "Some text", 0.01};
}
A tuple works well for a local result that will be unpacked immediately or when an API already uses tuple-like data. Otherwise, access such as std::get<0>(record) leaves readers to remember what each position means. The standard library documents both pair and tuple as ways to package multiple values (cppreference: std::tuple).
Unpack the returned object at the call site
Structured bindings in C++17 and later
Structured bindings give names to the elements of a returned pair or tuple. They also work with suitable aggregates, including structs with public data members:
const auto [quotient, remainder] = divide_pair(17, 5);
// C++17 or later
The declaration makes the components convenient to use; it does not create a second return channel. The function still returns one object. Structured bindings were introduced in C++17 (cppreference: Structured binding declaration).
Choose the binding names in the same order as the object’s elements. With a tuple or pair, callers depend on that order, so changing it can break their assumptions even if the types remain the same.
std::tie when the destination variables already exist
If variables are already declared, std::tie assigns tuple-like elements into them. It is available from <tuple> in C++11:
#include <tuple>
int quotient;
int remainder;
std::tie(quotient, remainder) = divide_pair(17, 5);
std::tie creates a tuple of lvalue references. Use std::ignore when a component is not needed:
bool inserted;
std::tie(std::ignore, inserted) = some_set.insert(value);
This approach requires destination lvalues to exist first. Microsoft Learn and cppreference document std::tie for tying references to tuple elements and ignoring selected values (Microsoft Learn: tuple functions; cppreference: std::tie).
When output-reference parameters make sense
A function can write results into references supplied by its caller:
Best Value
void divide_out(int dividend, int divisor,
int& quotient, int& remainder) {
quotient = dividend / divisor;
remainder = dividend % divisor;
}
This can be appropriate when the interface intentionally modifies caller-owned objects, reuses caller-provided storage, must preserve an established legacy signature, or is shaped by an ABI constraint. In ordinary new code, a returned aggregate tends to make the data flow easier to see: the result comes back as the function’s value rather than through parameters that are changed as a side effect.
If an output-parameter function can fail, its contract should make clear what happens to each output on failure. A returned result type can also represent status alongside values, but whichever design is chosen, document whether outputs are valid when the operation does not succeed.
Quick Recap
Compare the approaches
| Approach | Best fit | Main trade-off | Minimum language level |
|---|---|---|---|
Named struct |
Stable, self-documenting domain result | Requires defining a type | C++98 |
std::pair |
Exactly two naturally related values | Generic members are first and second |
C++98 |
std::tuple |
Fixed group of values, possibly of different types | Elements are positional | C++11 |
| Structured bindings | Readable unpacking of a returned object | Declaration syntax is unavailable before C++17 | C++17 |
std::tie |
Assigning into existing variables or ignoring elements | Requires predeclared lvalues | C++11 |
| Output references | Caller-owned storage or an intentional legacy interface | Mutation is less visible in the return value | C++98 |
Check these correctness details
- Return a value on every reachable path. A value-returning function that falls off the end has undefined behavior, except for the language’s specified cases such as
mainand certain coroutine rules (cppreference: return statement). - Do not return references to local variables. A local object ceases to exist when the function returns. Return an object by value, or return a reference only when the referenced object’s lifetime is guaranteed by the interface.
- Keep positional meanings stable. Callers using
std::get<N>, structured bindings, orstd::tierely on element order. Prefer named fields when that order is not self-evident or likely to change. - Match syntax to the selected standard.
std::pairis available from C++98,std::tupleandstd::tiefrom C++11, and structured bindings from C++17. Set the compiler’s language standard accordingly.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

