Modern C++
How To Write a Function That Returns More Than One Value
Like magic, tuples make the impossible possible. Yep, we're talking here about one little corner in the Standard Template Library that will make it simple to return multiple values from a single function, without "out" parameters -- and it has other uses, too.
- By Kate Gregory
- 06/22/2016
I'd like to show you some pretty useful templates from the Standard Template Library (STL) that enable you to get around the restriction that you can only return one value from a function.
There are more than 50 guidelines relating to functions (and that's not counting sections on special functions like constructors and destructors). They're all good, but I want to show you F.21: To return multiple "out" values, prefer returning a tuple or struct.
Before I explain what a tuple is, I want to address the implied assumption in that guideline. What are you being asked to prefer returning: a tuple or a struct over? What other option would you have? Typically, the other option is some sort of out parameter -- a parameter you take by non-const reference and change within the function. This means the calling code needs to create a variable first, and then pass it in by reference. It's relatively ugly and irritating, and relies on comments or API documentation so that the consumer of the function can use it properly. You end up with something like this:
int foo(int inValue, int& outValue)
{
outValue = inValue * 2;
return inValue * 3;
}
int main()
{
int number = 4;
int answer = foo(5, number);
return 0;
}
It's not clear here (or even in slightly less artificial functions) why one result of the function gets to be the return value and others have to be out parameters. Some developers use the return for a Boolean indicating success or failure, and out parameters for all the calculation results. Others use the return for the most important result, and out parameters for messages, success or failure flags, and secondary results. Still, others decide not to even have a return value and only use out parameters. We could argue all day about the best way to use out parameters, but I'm much happier telling you not to use out parameters at all.
If you're one of the many developers who doesn't like or use the STL, then your go-to alternative is to write a small class or struct to hold the multiple values you'd like to return. It's not hard, as you can tell by the code in Listing 1.
Listing 1: One Struct to Hold Multiple Values
struct twoNumbers
{
int value1;
int value2;
};
twoNumbers fooStruct(int inValue)
{
return twoNumbers{ inValue * 2, inValue * 3 };
}
int main()
{
int number, answer;
twoNumbers result = fooStruct(6);
number = result.value1;
answer = result.value2;
return 0;
}
This isn't awful, but it could result in your code being littered with oddly named little structs like twoNumbers and threeStrings and twoNumbersandaString and the like. If you never use them again, this is frustrating. Worse, if you have someone who uses encapsulation as a sort of ritual performance, they might insist on making these into classes -- with private member variables and public getters and setters -- bloating your code even further. If these odd little collections actually have a meaning -- they represent a quote, or an employee or a request -- then go ahead and make a class with a good name and use good names for the variables, too. But if they are one-off creations, then instead of a struct or a class, use a tuple.
What's a tuple? It's a template in the STL that extends the template called pair, which has been around since C++98. The pair template can be used to hold any two types that you want -- two integers, an integer and an Employee, whatever. A tuple is the same thing, except it can handle any number of types. It couldn't be added to the library until variadic templates were added to the language, but you don't have to care about that -- you can just take advantage of it.
With tuple in your life, you can get rid of the fairly pointless struct. However, accessing the members of a tuple can be a little unpleasant, so let me show you how to use pair when you know it's just two values. You can get access to pair from a number of headers, but because I'm about to go on to use tuple, let's include <tuple> to get everything we need at once.
Listing 2: A Pair Instead of a Struct
#include <tuple>
std::pair<int, int> fooPair(int inValue)
{
return std::make_pair(inValue * 2, inValue * 3 );
}
int main()
{
int number, answer;
auto resultPair = fooPair(7);
number = resultPair.first;
answer = resultPair.second;
return 0;
}
That's easy enough to do thanks to make_pair. And getting the values out is easy with the members called first and second. But if I suddenly need to return three integers, and switch to a tuple, the calling code gets kind of ugly, as shown in Listing 3.
Listing 3: Things Are Getting Ugly
std::tuple<int, int, int> fooThree(int inValue)
{
return std::make_tuple(inValue * 2, inValue * 3, inValue * -4);
}
int main()
{
int number, answer;
auto resultTuple = fooThree(8);
answer = std::get<0>(resultTuple);
number = std::get<1>(resultTuple);
int extra = std::get<2>(resultTuple);
return 0;
}
That call to get, with the index in angle brackets, and passing resultTuple as a parameter, is not intuitive at all. Luckily, there's a super handy template called tie that makes things much nicer. You can replace the main with this:
int main()
{
int number, answer, extra;
std::tie(answer, number, extra) = fooThree(9);
return 0;
}
That's much more readable. The guideline example uses tie, and so do I whenever I need to work with tuples. It's a one-line template that creates a tuple of references to its parameters. This returned tuple (from tie) is allowed on the right-hand side of the assignment. The tuple that comes back from the call to fooThree is then copied, element by element, into this tuple of references -- which changes the values of answer, number and extra in the calling scope. You get nice, simple calling code, you don't need to clutter your project with one-time-use structs, and your code documents itself.
There's plenty more to learn about tuples -- they're typesafe, for one thing, and won't let you put three integers into a tuple of "int, int, string" or tie a string reference to an Employee member of a tuple. The library writers have provided everything you need to copy them, compare them to each other and generally treat them like real types of their own. But you don't have to write them, as you would have to write those custom structs. They're incredibly useful. That's why I like this guideline so much. It solves a real problem we've all faced -- in a way that makes your code shorter, easier to write, easier for someone else to use -- and at the same time introduces you to a very useful corner of the Standard Template Library.
About the Author
Kate Gregory has been using C++ since before Microsoft had a C++ compiler, and has been paid to program since 1979. Kate runs a small consulting firm in rural Ontario, Canada, and provides mentoring and management consultant services, as well as writing code every week. She has spoken all over the world, written over a dozen books, and helped thousands of developers to be better at what they do. Kate is a Microsoft Regional Director, a Visual C++ MVP, an Imagine Cup judge and mentor, and an active contributor to StackOverflow and StackExchange sites. She develops courses for Pluralsight, primarily on C++ and Visual Studio. Since its founding in 2014 she is the Open Content Chair and speaker for CppCon, the definitive C++ conference.