

Function function return return values values*
source link: https://www.tuicool.com/articles/y6NfIrY
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

The latest C++ standard is now upon us, so it’s time to have a look at some of its new features.
To put one of the new features into context we’re going to have a look at – as the title suggests – multiple function return values
I should really distinguish between the following:
- A Subroutine (or Subprogram ) is a parameterised block of code that can be called multiple times from within a program.
- A Procedure is a subroutine that may have multiple input and/or output parameters and usually does not return a value. Procedures may change the state of the system
- A Function is a subroutine that has only input parameters and produces a return value. Functions are stateless – they will always produce the same result for the same inputs.
C++ programmers typically blur these distinctions (or ignore them). To keep with the C++ vernacular I will use the term ‘function’ to mean any of the above.
(*Sorry – this is a really terrible pun for a title)
The multiple-out-parameter dilemma
A C++ function has only one output parameter – the function return value. If we need more than one output from a function (for example, a value and an error code) many programmers will use an input-output parameter for one of the outputs
enum class Error { ok, failed }; double return_many(const double in_val, <strong>Error& err</strong>); // // err is an input-output parameter // but being used as an output.
Of course, there is nothing to stop us returning a single output value with multiple elements – a structure:
enum class Error { ok, failed }; struct Return_values { double value; Error error; }; Return_values return_many(const double in_val);
This function now has a much clearer intent . There is clean separation between function inputs and outputs. Many programmers resist this style of function, however, feeling it to be inefficient. Used correctly, though,this isn’t the case
int main() { <strong>Return_values result = return_many(14.6);</strong> if (result.error == Error::ok) { std::cout << result.value << std::endl; } }
We could even exploit Non-Static Data Member Initialisers (NSDMIs) for return value defaults
struct Return_values { double value <strong>{ };</strong> Error error <strong>{ Error::ok };</strong> }; Return_values return_many(const double in_val);
The (almost insignificant) wrinkle in all this is that the return value and error code are bound together in the same object. There may be occasions where we want their lifetimes to be independent, in which case we have to extract the individual parts.
int main() { Return_values result = return_many(14.6); Error error { result.error }; double val { result.value }; if (error == Error::ok) { std::cout << val << std::endl; } }
Tuple and tie
C++11 introduced std::tuple as a generic n-tuple type. We could choose to use a tuple as our return value type.
enum class Error { ok, failed }; <strong>std::tuple<double, Error></strong> return_many(const double in_val);
Our client code has to be modified now. Since tuples don’t have named elements (unless structs) we have to use a templated utility function – get<> – to extract each element.
int main() { std::tuple<double, Error> result = return_many(100.7); double value { <strong>std::get<0>(result)</strong> }; Error error { <strong>std::get<1>(result)</strong> }; if (error == Error::ok) { std::cout << value << std::endl; } }
I’m not sure that’s made our code any more readable. Maybe automatic type-deduction can help a little?
int main() { <strong>auto</strong> result = return_many(100.7); <strong>auto</strong> value = std::get<0>(result); <strong>auto</strong> error = std::get<1>(result); if (error == Error::ok) { std::cout << value << std::endl; } }
This is certainly less verbose, but I have to keep referring back to the function declaration to find out what the types of the result value object elements are.
From C++14, if your tuple has unique types for each of its elements you can specify the type of the element you want to extract instead.
int main() { auto result = return_many(100.7); auto value = <strong>std::get<double>(result)</strong>; auto error = <strong>std::get<Error>(result);</strong> if (error == Error::ok) { std::cout << value << std::endl; } }
If you don’t have unique types in your tuple your code won’t compile.
A simpler approach is to use some of the std::tuple helper functions.
std::tie assigns tuple values to local variables. It does this by creating a (temporary) tuple of references to the local objects, then assign from the right-hand tuple to the temporary. Since whatever happens to a reference happens to the original object, values from the right-hand tuple are assigned to the local objects. (for an excellent description of how std::tie works have a look here )
Notice, std::tie requires the same number of parameters – in the same order – as the source tuple. std::ignore can be used as a placeholder if you do not wish to retrieve a particular value from the tuple.
int main() { double value { }; auto result = return_many(100.7); <strong>std::tie(value, std::ignore) = result</strong>; // Ignore the error std::cout << value << std::endl; }
Or, more tersely.
int main() { double value { }; std::tie(value, std::ignore) = return_many(100.7); std::cout << value << std::endl; }
In case you were wondering (and to save you the typing) std::tie only works with std::tuples. The following code won’t compile.
enum class Error { ok, failed }; struct Return_values { double value { }; Error error { Error::ok }; }; Return_values return_many(const double in_val); int main() { double value { }; <strong>std::tie(value, std::ignore) = return_many(100.7); // ERROR! </strong> std::cout << value << std::endl; }
Structured bindings
Multiple output values via a struct (or tuple) has become an idiom of Modern C++. To improve the readability of code C++17 introduced the concept of Structured Bindings.
Structured bindings extend the mechanism of automatic type-deduction to allow multiple objects to have their types deduced from a tuple.
So, now we can write
enum class Error { ok, failed }; std::tuple<double, Error> return_many(const double in_val); int main() { <strong>auto [value, error ]</strong> = return_many(100.7); std::cout << value << std::endl; if (error == Error::ok) { std::cout << value << std::endl; } }
The number of objects must match the number of elements in the initialiser. There’s no way to ‘ignore’ a value if you’re not interested in it.
If an object isn’t used the compiler will emit a warning to that effect. To suppress this diagnostic you can use the [[ maybe_unused ]] attribute.
int main() { <strong>[[ maybe_unused ]]</strong> auto [value, error ] = return_many(100.7); std::cout << value << std::endl; // error isn't used in this code. The [[ maybe_unused ]] // attribute will suppress the compiler diagnostic // // if (error == Error::ok) { // std::cout << value << std::endl; // } }
The nice thing about structured bindings is they work not only with tuples, but on any struct-like construct. So, the following code works just fine.
enum class Error { ok, failed }; struct Return_values { double value { }; Error error { Error::ok }; }; Return_values return_many(const double in_val); int main() { auto [value, error ] = return_many(100.7); std::cout << value << std::endl; if (error == Error::ok) { std::cout << value << std::endl; } }
Structured bindings will also work with arrays (including std::array)
<strong>std::array<double, 4></strong> return_many(const double in_val); int main() { <strong>auto [val1, val2, val3, val4 ]</strong> = return_many(100.7); std::cout << val1 << std::endl; ... }
As with previously, you have to supply as many objects as there are initiliasers.
(And before you ask: no, this won’t work with unions)
Summary
Structured bindings in C++ provide a convenient syntax for returning (output) parameters from a function. This allows the programmer to make a clearer design intent about their function’s parameter usage. Being more explicit is always a better thing when programming.
If you’d like to build up, or expand, your C++ or design skills have a look at the following training links.
Recommend
-
27
-
15
TIL about functools.lru_cache - Automatically caching function return values in Python Oct 27, 2018 This is a short demonstration of how to use the functools.lru_cache
-
23
Named return values [best practice] yourbasic.org/golang In Go return parameters may be named and used as regular...
-
15
A quiz about type-coercion, indexOf return values, and the principle of lol whoops a bug.A quiz about type-coercion, indexOf return values, and the principle of lol whoops a bug. 03 Mar 2014 Given the following vers...
-
7
What is your take on checking return values? One day not so long ago, I was in a meeting listening to a team explain why their service had gone down and taken out a big chunk of a business. They were one of those things that has...
-
4
How to Return Several Values from a Function in C++ Published July 9, 2021 - 0 Comments Functions should take...
-
10
Here’s a list of all the types you can use in PHP type hinting, with the minimum PHP version since they are available: array function foo(array $bar)PHP 5.1+
-
16
We primarily use function parameters to get outside data inside a function for further processing. Similarly, we return values from a PHP function to get access to processed data outside the function. You can define functions in PHP with or withou...
-
6
In this Python tutorial, we will discuss how to return multiple values from a function in Python. Table Of Contents Let’s Dive into the tutorial.
-
3
How to Return Multiple Values from a Function in JavaScript 1437 views 2 years ago Javascript Return an...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK