File: lambda.yo

package info (click to toggle)
c%2B%2B-annotations 13.02.02-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 13,576 kB
  • sloc: cpp: 25,297; makefile: 1,523; ansic: 165; sh: 126; perl: 90; fortran: 27
file content (120 lines) | stat: -rw-r--r-- 5,771 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
em(Generic lambda expressions) hi(lambda: generic) may use tt(auto) to define
their parameters. When used, a lambda expression is instantiated by looking at
the actual types of arguments. Since tt(auto) is generic, different parameters
defined using tt(auto) can be instantiated to different types. Here is an
example (assuming all required headers and namespace declaration were
specified):
    verbinclude(-ns4 //code examples/genlambda.cc)
    The generic lambda function is defined in lines 3 through 6, and is
assigned to the tt(lambda) identifier. Then, tt(lambda) is passed to
tt(accumulate) in lines 12 and 13. In line 12 it is instantiated to add
tt(int) values, in line 13 to add tt(std::string) values: the same tt(lambda)
is instantiated to two completely different functors, which are only locally
available in tt(main).

Generic lambda expressions are in fact class templates. To illustrate: the
above example of a generic lambda expression could also have been implemented
using the following class template:
        verb(    struct Lambda
    {
        template <typename LHS, typename RHS>
        auto operator()(LHS const &lhs, RHS const &rhs) const 
        {
            return lhs + rhs;
        }
    };
    auto lambda = Lambda{};)

This identity implies that using tt(auto) in the lambda expression's
parameter list obeys the rules of template argument deduction (cf. section
ref(TEMPFUNARGS)), which are somewhat different from the way tt(auto) normally
operates.

Another extension is the way generic lambda expressions capture outer scope
variables.  Previously variables could only be captured by value or by
reference. As a consequence an outer scope variable of a type that only
supports move construction could not be passed by value to a lambda
function. This restriction was dropped, allowing variables to be initialized
from arbitrary expressions. This not only allows move-initialization of
variables in the lambda introducer, but with generic lambdas variables may
also be initialized if they do not have a correspondingly named variable in
the lambda expression's outer scope. In this case initializer expressions can
be used as follows:
        verb(    auto fun = [value = 1] 
               {
                   return value;
               };)

This lambda function (of course) returns 1: the declared capture deduces
the type from the initializer expression as if tt(auto) had been used.
    
    To use move-initialization tt(std::move) should be used. E.g., a
em(unique_ptr) only supports move-assignment. Here it is used to return one of
its values via a lambda function:
        verb(    std::unique_ptr<int> ptr(new int(10));
    auto fun = [value = std::move(ptr)] 
               {
                   return *value;
               };)

In generic lambda expressions the keyword tt(auto) indicates that the compiler
determines which types to use when the lambda function is instantiated. A
generic lambda expression therefore em(is) a class template, even though it
doesn't look like one. As an example, the following lambda expression defines
a generic class template, which can be used as shown:
        verb(    char const *target = "hello";

    auto lambda =         
       [target](auto const &str)
       {
           return str == target;
       };
       
    vector<string> vs{stringVectorFactory()};

    find_if(vs.begin(), vs.end(), lambda);)

This works fine, but if you define tt(lambda) this way then you should be
prepared for complex error messages if the types of the derefenced iterators
and lambda's (silently assumed) tt(str) type don't match.

Here is a little program illustrating how generic lambda expressions can be
used in other generic lambda expressions: class templates could also have been
used. In lines 1 through 9 a generic lambda expression tt(accumulate) is
defined, defining a second paramater which is used as a function: its argument
therefore should be usable as a function. A functor definitely is, an the
second generic lambda expression tt(lambda), defined in lines 11 through 14
provides it. In line 21 tt(accumulate) and tt(lambda) are instantiated so that
they operate on (a vector of) tt(ints), in line 22 they are instantiated for
processing (a vector of) tt(strings):
    verbinsert(-ns4 //code examples/genlambda2.cc)

In some situations generic lambdas are a bit too generic, resulting in verbose
implementations which are not required by templates in general. Consider a
generic lambda that specifies an tt(auto &it) parameter, but in addition it
should specify a parameter tt(value) of type tt(ValueType) that should be
defined by tt(it's) class. Such a parameter requires the use of a tt(decltype)
(and maybe also the use of tt(std::decay_t)) to retrieve tt(it's) actual
type. Inside the lambda's body a tt(using) declaration can be specified to
make the type available, but that again requires a verbose specification using
tt(std::decay_t) and tt(decltype). Here is an example:
        verb(    auto generic = 
        [](auto &it, 
           typename std::decay_t<decltype(it)>::ValueType value)
        {
            using Type = std::decay_t<decltype(it)>;
            typename Type::ValueType val2{ value };
            Type::staticMember();
        };)

To avoid this kind of verbosity generic lambda functions can also be defined
like ordinary templates, in which case the template header immediately follows
the lambda-introducer. Using this alternative form the definition of the
tt(generic) generic lambda simply and straightforwardly becomes:
        verb(    auto generic = 
        []<typename Type>(Type &it, typename Type::ValueType value)
        {
            typename Type::ValueType val2{ value };
            Type::staticMember();
        };)