Function objects and lambda expressions

advertisement
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
288
Function objects and lambda expressions
• function objects are objects of class type with the function call operator overloaded
– operator() must be a non-static member function
• important for effective use of the standard library
– enables algorithms to work with both ordinary functions and function objects
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f);
for_each(begin(v), end(v), fun);
for_each(begin(v), end(v), fun_obj{});
– increases the expressive power of the library
• function objects are more flexible and powerful than functions
– can have data members to keep a state
– can have other member functions than operator()
– makes the resulting code more effective
transform(begin(v), end(v), std::negate<double>{});
// negate object can be inlined
• lambda expressions is a convenient way to create simple function objects inplace
– in many situations in C++98 where function objects was used, lambda expressions should be used in C++11
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
289
Regarding exam
• important to have a good understanding of function objects and lambda expressions
– know about the standard library function objects
– be able to define own function objects when needed
– sufficient practice in using function objects, both from standard library and your own, in combination with other components
– lambdas is often a good alternative to create simple function objects
• cplusplus.com Reference will be available at exam (web browser)
• see the course examination page for more information
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
290
Defining function object class types
• defining a simple, binary function object class
template<typename T>
struct logical_xor
{
constexpr bool operator()(const T& lhs, const T& rhs) const
{
return (lhs && !rhs) || (!lhs && rhs);
}
using first_argument_type = T;
using second_argument_type = T;
using result_type = bool;
};
– constexpr allows locical_xor be evaluated during compile-time, for appropriate arguments
– constexpr also implies inline
– the alias declarations (type definitions) are required by some library components, e.g., for declaring function parameters and other stuff
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
291
Using function objects
#include <iostream>
#include <functional>
#include <iterator>
#include <vector>
using namespace std;
int main()
{
vector<bool> b1{ false, false, true, true };
vector<bool> b2{ false, true, false, true };
vector<bool> b3;
transform(begin(b1), end(b1), begin(b2), back_inserter(b3), logical_xor<bool>{});
// b3 now contain b1 xor b2 (bit-wise)
return 0;
}
• the expression logical_xor<bool>{} creates a temporary object
– passed to the corresponding parameter, binary_op
– the function call in transform is
binary_op(*first1, *first2);
– can be either an ordinary function, a function object, or a lambda expression
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
292
Function objects with state
• Fibonacci number generator (F0 = 0, F1 = 1, Fn = Fn-1 + Fn-2 for n>1)
class Fibonacci
{
public:
Fibonacci() : Fn{0}, Fn_1{1} {}
unsigned long long int operator()() const
{
unsigned long long int next{Fn};
Fn = Fn_1;
Fn_1 = next + Fn;
return next;
}
private:
mutable unsigned long long int Fn;
mutable unsigned long long int Fn_1;
};
vector<unsigned long long int> v(20);
generate(begin(v), end(v), Fibonacci{});
File: Standard-Function-Objects-OH-en
// 0, 1, 1, 2, 3, 5, 8, …
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
293
Standard library function object classes
• the standard library declares function objects classes correspond to the arithmetic, comparison and logical operators.
plus
minus
multiplies
divides
modulus
negate
addition
subtraction
multiplication
division
reminder from integer division
negation – performs
x +
x x *
x /
x %
-x
equal_to
not_equal_to
greater
less
greater_equal
less_equal
equality
inequality
greater-than
less-than
greater-or-equal
less-or-equal
x
x
x
x
x
x
logical_and
logical_or
logical_not
logical conjunction
logical disjunction
logical negation
x && y
x || y
!x
bit_and
bit_or
bit_xor
bit-wise and
bit-wise or
bit-wise xor
x & y
x | y)
x ^ y)
y
y
y
y
y
== y
!= y
> y
< y
>= y
<= y
• makes it possible to pass “operators” as function arguments, and invoke them using ordinary function call syntax
transform(begin(v1), end(v1), begin(v2), back_inserter(v3), plus<int>{});
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
294
Standard library function object class definitions (1)
• std::plus is defined (default argument void is C++14)
template<typename T = void> struct plus
{
constexpr T operator()(const T& lhs, const T& rhs) const
{
return lhs + rhs;
}
using first_argument_type = T;
using second_argument_type = T;
using result_type = T;
};
– wrapper for operator+
– works for all types T having operator+ and copy construction
– implicit specialization (T = void) is illegal!
– explicit specialization for a specific type creates a simple instance with all T:s replaced with that type – plus<int>
• unary function objects should have the following two nested type definitions, instead of those three above
using argument_type = …;
using result_type = …;
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
295
Standard library function object class definitions (2)
• explicit (standard library) specialization for std::plus<void> (C++14)
template<> struct plus<void>
{
template <typename T, typename U>
constexpr auto operator()(T&& lhs, U&& rhs) const
-> decltype(std::forward<T>(lhs) + std::forward<U>(rhs))
{
return std::forward<T>(lhs) + std::forward<U>(rhs);
}
typedef unspecified is_transparent;
};
• parameter types and return types are deduced – arguments can be different, compatible types
• member type is_transparent indicates that this is a transparent function object
– accepts arguments of arbitrary types
– uses perfect forwarding
– avoids unnecessary copying and conversion when used in heterogeneous context, or with rvalue arguments
string a = "Hello ";
const char* b = "world";
cout << plus<>{}(a, b) << ’\n’;
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
296
Lambda expressions
Lambda expressions provide a quick way to create (simple) function objects at their point of use
array<int, 9> a{ 7, -2, -8, 4, 3, -5, 9, -1, 6 };
sort(begin(a), end(a), [](int x, int y) { return abs(x) < abs(y); });
• [] is the lambda introducer – introduces the declaration of a lambda expression
– can contain zero or more lambda captures
– [] only global and static entities can be referenced in the lambda – capture-less lambda expression
– [&] will capture any name used from the reaching scope implicitly by reference
– [=] will capture by value
– [x] explicitly capture the object named x, as read only
– [&x] will capture x by reference
– [=, &x] default capture is by value, x is captured by reference
– [this] will allow capturing members
• () contains ordinary parameter declarations
– can be left out if no parameters
• {} is a compound statement
• The return type is in the example above is implicitly deduced from the type of the returned value – it can be declared explicitly
[](int x, int y) -> bool { return abs(x) < abs(y); }
• there is more…
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
297
Closure objects
• The evaluation of a lambda expression creates a closure object
– a prvalue temporary
– behaves as a function object
• closure types are not specified but there are two easy ways to store closure objects
– auto
auto fun =[](int x, int y) { return abs(x) < abs(y); };
cout << fun(2, 1) << endl;
– template std::function (introduced later)
function<bool(int, int)>
func = [](int x, int y) { return abs(x) < abs(y); };
cout << func(1, 2) << endl;
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
298
Non-generic lambda expressions and closure types
auto lambda = [](int a, int b) { return a < b; }
bool (*fp)(int, int) = lambda;
// pointer-to-function conversion allowed for capture-less lambdas
The closure type for a non-generic lambda expression has a public operator() (1)
• same parameter types and return type as the lambda expression
• const if the lambda-expression’s parameter declaration clause is not followed by mutable
Capture-less lambdas have a pointer to function conversion (2)
• parameter types and return type are the same as for the function call operator
• returns the address of a function (3) that, when invoked, has the same effect as invoking the function call operator
class Closure {
public:
bool operator()(int a, int b) const { return a < b; }
(1)
private:
static bool invoker_(int a, int b) { return a < b; }
(3)
using fptr_t = bool (*)(int, int);
public:
operator fptr_t() const { return &invoker_; }
};
File: Standard-Function-Objects-OH-en
(2)
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
299
Generic lambda expressions (C++14)
auto lambda = [](const auto& a, const auto& b) { return a + b; };
• auto indicates a generic lambda parameter
• closure operator() is a member template with one invented type template parameter for each occurrence of auto in the lambda
struct Closure
{
template<typename T, typename U>
auto operator()(const T& x, const U& y) const
{
return x + y;
}
};
– auto is used for the deduced return type of operator()
• conversion from a capture-less generic lambda to an appropriate pointer-to-function is allowed
long (*fp)(long, long) = lambda;
– parameter types and also return type may differ
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
300
Generic lambda expressions and closure types (C++14)
auto lambda = [](const auto& a, const auto& b) { return a + b; }
Complete closure type for a capture-less lambda expression:
struct Closure {
template<typename T, typename U>
auto operator()(const T& x, const U& y) const { return x + y; }
private:
template<typename T, typename U>
static auto invoker_(const T& x, const U& y) { return x + y; }
template<typename T, typename U> using fptr_t =
decltype(invoker_(declval<T>{}, declval<U>{})) (*)(T, U);
public:
template<typename T, typename U>
operator fptr_t<T, U>() const { return &invoker_; }
};
• function template declval returns an rvalue reference (T&& and U&&, respectively) without referring to any object
– references to (non-exciting) values of type T and U is used in the unevaluated call of invoker_ in decltype(…)
– the return type of fptr_t is deduced this way
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
301
Generic lambda expressions for function parameter packs (C++14)
The invented type template-parameter is a parameter pack if the corresponding parameter-declaration declares a function parameter pack.
auto lambda = [](auto&&... fpp){ … };
// function parameter pack
Closure type:
struct Closure {
public:
template<typename... Args>
auto operator()(Args&&... args) { … }
// parameter pack
…
};
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
302
Function call syntax (1)
There are three ways to invoke a function f on a class object of type T:
f(x)
Syntax #1: f is a normal function or function object and x is an object of type T
x.f()
Syntax #2: f is a member function and x is an object of type T or a reference to an object of type T
p->f()
Syntax #3: f is a member function and p is a pointer to an object of type T
Suppose we have a unary function that can operate on objects of type T, and a container with elements of type T
void fun(T& t);
vector<T> v;
• to apply fun to each object in the vector we can use the algorithm for_each
for_each(begin(v), end(v), fun);
// Fine, for_each uses call syntax #1
• if fun instead is a member of T and we try the following, it will not work
for_each(begin(v), end(v), &T::fun);
// Compile error, call syntax #2 required
• the third variant would be if the container stores pointers to T, and fun is a member of T, which also will not work
vector<T*> v;
for_each(begin(v), end(v), &T::fun);
File: Standard-Function-Objects-OH-en
// Compile error, call syntax #3 required
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
303
Function call syntax (2)
• the implementation of for_each shows why the passed function f cannot be a member function, unless adapted
template<typename InputIterator, typename Function>
Function
for_each(InputIterator first, InputIterator last, Function f)
{
for (; first != last; ++first)
f(*first);
// syntax #1
return f;
}
– works with both ordinary functions and function objects, but not for calling member functions
– all standard library algorithms use this normal function call syntax
– to be able to call a member function we must adapt the member function call syntax to ordinary function call syntax
x.memfun()
–>
f(x)
x.memfun(y)
–>
f(x, y)
• an ordinary function also need adaption, if a component require typedefs for argument type(s) and return type
– for_each does not
Before finding att how to solve this, lets find out what can be called…
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
304
Callable objects (1)
Something that can be called as a function.
• functions, function pointers, or function references
void fun(int x) { cout << x << endl; }
// function
void (*fp)(int){ fun };
// function pointer (bound to fun above)
void (&fr)(int){ fun };
// function reference (bound to fun above)
fun(1);
fp(2);
fr(3);
// 1
// 2
// 3
• objects implicitly converted to function pointer or to function reference
struct Type
{
using Fun_Ptr = void (*)(int);
operator Fun_Ptr() const { return fun; }
};
// alt. void (&)(int)
// conversion function – fun defined above
Type obj;
obj(4);
// 4
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
305
Callable objects (2)
• function object
struct Fun_Obj_Type
{
void operator()(int x) const { cout << x << endl; }
};
Fun_Obj_Type obj;
obj(6);
// 6
• closure object – behaves as a function object
auto f = [](int x){ cout << x << endl; };
f(7);
File: Standard-Function-Objects-OH-en
// 7
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
306
Callable objects (3)
• pointer to non-static member function
struct Type
{
void mem_fun(int x) const { cout << x << endl; }
};
auto pmf = &Type::mem_fun;
// void (Type::*pmf)(int) const = &Type::mem_fun;
Type obj;
(obj.*pmf)(8);
// 8
• to point to a static member function, an ordinary function pointer must be used
struct Type
{
static void smem_fun(int x) { cout << x << endl; }
};
auto fp = &Type::smem_fun;
// void (*fp)(int) = &Type::smem_fun;
Type obj;
(fp)(9);
File: Standard-Function-Objects-OH-en
// 9
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
307
Function call wrappers
• negators
– function call wrappers to negate the result from calling a predicate function
– not1() helper function returning a function call wrapper for a unary predicate
– not2() helper function returning a function call wrapper for a binary predicate
• function argument binder
– bind() returns a function call wrapper which binds a callable object and arguments
• member function adaptor
– mem_fn() returns a function call wrapper to call a member function using normal function call syntax
• functions as first class citizens
– function is a polymorphic function wrapper that can store, copy, and call arbitrary callable objects
Note: These components are all defined using the new feature variadic template.
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
308
Negators (1)
• not1()
struct less_than_5
// a user defined unary predicate
{
constexpr bool operator()(int x) const { return x < 5; }
typedef int argument_type;
typedef bool return_type;
};
vector<int> v{ 1, 8, 3, 6, 10, 7, 5, 9, 2, 4 };
cout << count_if(begin(v), end(v), not1(less_than_5{})) << endl;
– the function object returned by not1() is applied to each element in v by transform
– calls less_than_5{} and returns the result negated
• not2()
transform(begin(v1), end(v1), begin(v2), begin(v3), not2(std::less<int>{}));
– the function object returned by not2() is applied to each pair of elements in v1 and v2 by transform
– calls std::less<int>{} and returns the result negated
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
309
Negators (2)
Utility function not1() takes a unary predicate Predicate and returns a function object of type unary_negate
template<typename Predicate>
unary_negate<Predicate> not1(const Predicate& pred)
{
return unary_negate<Predicate>(pred);
}
• Predicate must have nested type argument_type
template<typename Predicate> class unary_negate
{
public:
explicit unary_negate(const Predicate& x) : pred(x) {}
bool operator()(const typename Predicate::argument_type& x) const { return !pred(x); }
using argument_type = typename Predicate::argument_type;
using result_type = bool;
protected:
Predicate pred;
};
– defined by standard function objects
– defined by std::function when wrapping a unary or binary function
– defined by std::mem_fn when wrapping a unary or binary member function
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
310
Utility function std::bind
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
using namespace std::placeholders;
vector<int> v{ 0, 0, 1, 2, 3, 3, 4, 5, 5, 5, 6, 7, 8, 9, 9 };
// not necessarily sorted
• how many values in v are greater than 6?
auto n = count_if(begin(v), end(v), bind(greater<int>{}, _1, 6));
• bind is given a binary predicate, greater<int>{}, and the arguments to be bound
– the second parameter is bound to 6 – greater<int>()::operator(?, 6)
– argument for the first parameter is to be supplied when count_if calls the unary predicate created by bind
if (bind(greater<int>{}, _1, 6)(*it))
// effectively if (greater<int>{}(*it, 6))
– placeholder _1 refers to the first argument given in the call of the function call wrapper (*it)
– in this case there is only one argument – the only valid placeholder is _1
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
311
Utility function std::mem_fn
class C {
public:
C(int i = 0) : value_(i) {}
int times(int n) const { return n * value_; }
operator int() const { return value_; }
private:
int value_;
};
int a[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
vector<C> v(begin(a), end(a));
// initialize C objects with values from a
transform(begin(v), end(v), begin(a), ostream_iterator<int>{cout," "}, mem_fn(&C::times));
• mem_fn covers all member function call variants – the call to transform would be the same if v was storing pointers to C
vector<C*> v{ new C{1}, new C{2}, new C{3} };
• not restricted to member functions with just none or one parameter
• nested types
– result_type and argument_type are defined for member functions with no parameters
– result_type, first_argument_type, and second_argument_type are defined for member functions with one parameter
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
312
Template std::function (1)
• function wrapper class that can store, copy, and call arbitrary callable objects, such as
– normal functions
– function objects
– closure objects (acts as function objects)
– bind expressions
• allow functions to be first-class objects, that is, can be
– assigned
– passed by value
– returned by value
– stored in data structures
• exception bad_function_call is thrown by function::operator() when the function wrapper object has no target
class bad_function_call : public std::exception {
public:
bad_function_call() noexcept;
};
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
313
Template std::function (2)
function<double(double)> fn;
// no function bound – throws bad_function_call, if called
fn = std::sinf;
// normal function, signature float(float)
cout << fn(3.14) << endl;
// cos is overloaded in three version, for float, double, long double, type conversion required:
fn = static_cast<double(*)(double)>(std::cos);
cout << fn(3.14) << endl;
fn = [](double d) -> double { return 2 * d; };
// closure object (lambda expression)
cout << fn(3.14) << endl;
function<bool(int)>
pred{ bind(std::greater<int>(), _1, 10) };
// bind expression
cout << boolalpha << pred(9) << ", " << pred(11) << endl;
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
314
Template std::function (3)
If instantiated for a unary or binary function:
• have type definitions for result type and argument type(s)
– for unary functions
argument_type
result_type
– for binary functions
first_argument_type
second_argument_type
result_type
• expected by some components
char* a[]{ "C++", "Ada", "Basic", "C", "C++", "Eiffel", "Java", "Python", "C++" }
count_if(begin(a), end(a), not1(function<int(const char*)>{bind(strcmp, "C++", _1)}))
– not1 require those types
– bind does not supply
– we can use function inbetween to supply the types
– a lambda expression is much simpler to use in many cases, e.g in this case:
count_if(begin(a), end(a), [](const char* s){ return !(strcmp("C++", s)); })
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
315
Template std::function (4)
Definition, parts of – just to give a hint on implementation:
template<class> class function;
// primary – undefined
template <class R, class... ArgTypes>
class function<R(ArgTypes...)> {
public:
...
function();
function(const function&);
function(function&&);
...
// result type, argument types
// specialization for this function signature
function& operator=(const function&);
function& operator=(function&&);
...
template<class F> function(F);
...
// constructor taking function
explicit operator bool() const noexcept;
// true if *this has a target, otherwise false
R operator()(ArgTypes...) const;
// function call operator
...
};
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
TDDD38 APiC++
Standard Library – Function objects and lambda expressions
316
Summing up: Containers – Iterators – Algorithms – Functions objects
Designed for flexibility, extensibility, reuse and efficiency.
• uniform interfaces
– makes containers and iterators interchangeable
– many algorithms can be applied to a variety of structures
– makes learning and use easier
• iterators is the “glue” that allow for combining components
– algorithms operate on data through iterators
– containers provide iterators
– iterators can be bound to streams
– ordinary pointers can in many cases be used where iterators are expected
• function objects can be used for many different purposes
– algorithms and containers can take a function object to modify their behaviour
– function wrappers can be used to create more complicated operations from simple functions or function objects, e.g.
– function call adaptors
– reference wrapper
– use lambda expressions for simple function objects
• utilities for supporting rvalue references, move semantics, perfect forwarding, …
• the template mechanism is heavily used, supports together with e.g. variadic templates
– reusability, genericity, flexibility, efficiency
File: Standard-Function-Objects-OH-en
© 2015 Tommy Olsson, IDA, Linköpings universitet (2015-04-23)
Download