Skip to content
C++ Better Explained
Go back
How to Return Multiple Values from a C++ Function: 5 Practical Methods
Edit page

How to Return Multiple Values from a C++ Function

A C++ function can only return one value. But real programs constantly need functions to produce multiple results — the minimum and maximum of a list, a quotient and remainder, a status code and message.

Here are five clean ways to handle this, from simplest to most flexible.


Method 1: Return a Struct (Most Readable)

Define a small struct to bundle related values together:

#include <iostream>
using namespace std;

struct MinMax {
    int min;
    int max;
};

MinMax findMinMax(int a, int b, int c) {
    MinMax result;
    result.min = min({a, b, c});
    result.max = max({a, b, c});
    return result;
}

int main() {
    MinMax mm = findMinMax(7, 2, 9);
    cout << "Min: " << mm.min << endl;  // 2
    cout << "Max: " << mm.max << endl;  // 9
    return 0;
}

This is the most readable approach. The returned struct is self-documenting — mm.min and mm.max tell you exactly what each value means.

Use this when the returned values have a meaningful relationship and you’ll use them together more than once.


Method 2: Return a std::pair (Quick for Two Values)

For exactly two values, std::pair from <utility> is built-in and requires no extra struct definition:

#include <iostream>
#include <utility>
using namespace std;

pair<int, int> divideWithRemainder(int a, int b) {
    return {a / b, a % b};
}

int main() {
    auto result = divideWithRemainder(17, 5);
    cout << "Quotient:  " << result.first  << endl;  // 3
    cout << "Remainder: " << result.second << endl;  // 2
    return 0;
}

The downside: .first and .second aren’t descriptive. A struct with named fields is usually clearer unless the values are very obvious from context.

C++17 structured bindings make this much nicer:

auto [quotient, remainder] = divideWithRemainder(17, 5);
cout << quotient << " remainder " << remainder << endl;

Method 3: Return a std::tuple (Three or More Values)

When you need to return three or more values without defining a struct, std::tuple works:

#include <iostream>
#include <tuple>
#include <string>
using namespace std;

tuple<string, int, double> getStudentInfo() {
    return {"Alice", 20, 3.8};
}

int main() {
    auto [name, age, gpa] = getStudentInfo();  // C++17 structured bindings
    cout << name << ", age " << age << ", GPA " << gpa << endl;
    return 0;
}

Before C++17, you’d use get<0>(), get<1>(), etc. — which is awkward. Structured bindings made tuples much more usable.

Still, if you find yourself returning four or more values regularly, a struct is probably cleaner and more maintainable.

If you're looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you're a complete beginner or looking to solidify your understanding. Just $19.

Method 4: Output Parameters (Pass by Reference)

The classic C-style approach: pass variables by reference and have the function fill them in:

#include <iostream>
#include <vector>
using namespace std;

void findMinMax(const vector<int>& nums, int& outMin, int& outMax) {
    outMin = nums[0];
    outMax = nums[0];
    for (int n : nums) {
        if (n < outMin) outMin = n;
        if (n > outMax) outMax = n;
    }
}

int main() {
    vector<int> data = {4, 8, 1, 9, 3, 7};
    int minVal, maxVal;

    findMinMax(data, minVal, maxVal);

    cout << "Min: " << minVal << endl;  // 1
    cout << "Max: " << maxVal << endl;  // 9
    return 0;
}

This works, but it’s less idiomatic in modern C++. The caller has to declare variables beforehand, and the function signature doesn’t make clear which parameters are inputs and which are outputs.


Method 5: Aggregate Initialization (Clean Modern Style)

In modern C++, returning a struct is even more concise with aggregate initialization:

#include <iostream>
#include <string>
using namespace std;

struct ParsedInput {
    bool valid;
    int value;
    string error;
};

ParsedInput parseNumber(const string& input) {
    try {
        int n = stoi(input);
        return {true, n, ""};
    } catch (...) {
        return {false, 0, "Invalid number: " + input};
    }
}

int main() {
    auto result = parseNumber("42");
    if (result.valid) {
        cout << "Got: " << result.value << endl;
    }

    auto bad = parseNumber("abc");
    if (!bad.valid) {
        cout << bad.error << endl;
    }

    return 0;
}

This pattern — returning a struct that includes a success/error indicator along with the result — is very common in real codebases.


Quick Comparison

MethodBest forReadability
Struct2+ related values used together★★★★★
std::pairExactly two values, quick and built-in★★★☆☆
std::tuple3+ values without a dedicated struct★★★☆☆
Output parametersCompatibility with older C++ code★★☆☆☆

For new code, the struct approach is almost always the clearest. pair is fine for simple two-value returns. Avoid output parameters unless you’re working with legacy code.



Take Your C++ Further

If you’re looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you’re a complete beginner or looking to solidify your understanding. Just $19.

👉 Get the C++ Better Explained Ebook — $19

📋

Free Download: The 10 Mistakes Every C++ Beginner Makes

A free 1-page checklist that shows the exact traps that slow down every C++ beginner — so you can avoid them from day one.

🔒 No spam. Unsubscribe anytime.


Edit page
Share this post on:

Previous Post
C++ Iterators Explained: How to Traverse STL Containers for Beginners
Next Post
C++ stringstream Tutorial: Parse and Build Strings Like a Pro