cppannotations/annotations/yo/first/binding.yo
2017-11-15 19:40:38 +01:00

120 lines
4.1 KiB
Text

Usually functions return single-valued results: tt(doubles, ints, strings),
etc. When functions need to return multiple values a em(return by argument)
construction is often used, where addresses of variables that live outside of
the called function are passed to functions, allowing the functions to assign
new values to those variables.
When multiple values must be returned a tt(struct) can be used, but a
tt(std::pair) (cf. section ref(PAIR)) or a tt(std::tuple) (cf. section
ref(TUPLES)) can also be used. Here's a simple example, where a function
tt(fun) returns a tt(struct) having two data fields:
verb(
struct Return
{
int first;
double second;
};
Return fun()
{
return Return{ 1, 12.5 };
}
)
(Briefly forward referencing to sections ref(PAIR) and ref(TUPLES): the
tt(struct) definition could be omitted completely if tt(fun) would return a
tt(pair) or tt(tuple). In those cases the following code remains valid.)
Another function em(calling) tt(fun) traditionally defines a variable
of the same type as tt(fun's) return type, and then uses that variable's
fields to access tt(first) and tt(second). If you don't like the typing,
tt(auto) can also be used:
verb(
int main()
{
auto r1 = fun();
cout << r1.first;
}
)
Since C++17 standard this syntax has been extended this by introducing
em(structured binding declarations). Here, tt(auto) is followed by a (square
brackets surrounded) comma-separated list of variables, where each variable is
em(defined), and receives the value of the corresponding field or element of
the called function's return value. So, the above tt(main) function can also
be written like this:
verb(
int main()
{
auto [one, two] = fun();
cout << one; // one and two: now defined
}
)
Structured binding declarations can also be used in combination with
reference variables. The following defines tt(rone) and tt(rtwo) as,
respectively tt(int &&rone) and tt(double &&rtwo):
verb(
int main()
{
auto &&[rone, rtwo] = fun();
}
)
If the called function returns a value that survives the function-call
itself, then em(lvalue references) can structured binding declarations can
also be used to define lvalue reference variables. E.g.,
verb(
Return &fun2()
{
static Return ret{4, 5};
return ret;
}
int main()
{
auto &[lone, ltwo] = fun2(); // OK: referring to ret's fields
}
)
When initializing structured binding declarations functions returning
multiple values in a tt(struct) are not required. The tt(struct) can also
anonymously be defined:
verb(
int main()
{
auto &[lone, ltwo] = Return{4, 5};
}
)
The tt(struct) doesn't even have to be defined by itself. In section
ref(PAIR) we encounter the tt(std::pair) container that can be used to define
pairs of values, and in section tt(TUPLES) its generalized variant (the
em(tuple)) is encountered.
Another application is found in situations where nested statements of
tt(for) or selection statements benefit from using locally defined variables
of various types. Such variables can easily be defined using structured
binding declarations that are initialized from anonymous structs, pairs or
tuples. Here is an example illustrating this:
verb(
// define an appropriate struct (or --in time-- a pair or tuple):
struct Three
{
size_t year;
double firstAmount;
double interest;
};
// next define a for-statement using local variables of
// different types.
for (
auto [year, amount, interest] = Three{0, 1000, .03};
year <= 10;
++year, amount *= (1 + interest)
)
cout << "Year " << year << ": amount = " << amount << '\n';
/* Abbreviated output:
Year 0: amount = 1000
Year 1: amount = 1030
Year 2: amount = 1060.9
...
Year 9: amount = 1304.77
Year 10: amount = 1343.92
*/
)