Function Objects or Functors are widely used in std library and serve in various modes such as passing ref objects, wrapping up function objects, accessing member functions etc.
Details
Functors
A functor is an object or structure that can be called like a function by overloading the function call operator () . Functors also wrap a state during construction which can be used in function calls.
The following shows a functor implementation.
struct Greet { void operator()() { time_t curr_time; curr_time = time(NULL); tm *tm_local = localtime(&curr_time); size_t h = tm_local->tm_hour; cout << "Hello, "; if (h > 4 && h < 12) cout << "Good Morning"; else if (h > 11 && h < 17) cout << "Good Afternoon"; else if (h > 16 ) cout << "Good Evening"; cout << "!" << endl; } } g;//Example
g(); //prints @ 2 am : Hello, Good Evening!
Lambda Expression
Lambdas are basically anonymous function objects generated right at the location where it's invoked or passed as an argument to a function. Lambdas are game changer since they seamlessly integrate into stl algorithms, concurrency objects and more.
The syntax for Lambdas is as below.
[capture] (parameters)optional mutableoptional exception-Specoptional -> trailing return Typeoptional
{
definition of method
}
Except for the lambda introducer ([]) that contain captured local variables, all of the other items in the syntax are optional.
Following is a minimum lambda expression, calling itself.
[] //introducer
// no parameters {time_t curr_time;curr_time = time(NULL);tm *tm_local = localtime(&curr_time); size_t h = tm_local->tm_hour; cout << "Hello, "; if (h > 4 && h < 12) cout << "Good Morning"; else if (h > 11 && h < 17) cout << "Good Afternoon"; else if (h > 16 ) cout << "Good Evening"; cout << "!" << endl;
} //body(); //calling itself!
//prints @ 3 am : Hello, Good Evening!
The following describes it in detail:
- capture clause (Also known as the lambda-introducer)
- parameters (Also known as the lambda declarator)
- mutable specification.
- exception-specification.
- trailing-return-type.
- lambda definition.
capture
Unlike a normal function, a lambda can also to refer to local variables. It can access them either as read only value or as a reference as described below.
- [=] captures all local variables as value readonly
- [&] captures all local variables as value readwrite
- [i,j,&k] captures local variables i and j as value readonly and k as value readwrite
- [=,&i] captures all local variables as value readonly except i
- [&,=i] captures all local variables as value readwrite except i
- [this] captures all local variables as value readonly
- [v...] captures variadic variables
If the captured variables are modified, value captured variables do not retain modified value and revert back to original after the call; reference captured variables retain modified values after the call.
Global variables and static variables need not be captured. They can be accessed as is.
This is demonstrated in the example 2.
parameters
Parameters for a lambda are similar to those of normal functions,
mutable
This keyword should be used if value captured variables are modified in the implementation.
exception specification
noexcept exception specification can be used to indicate that the lambda expression doesn't throw any exceptions. As with ordinary functions, the compiler generates warning if a lambda expression declares the noexcept exception specification and the lambda body throws an exception.
//compiler warnings []() noexcept { throw 5; }();
trailing return type
The return type for a lambda is specified using a C++ feature named trailing return type. This specification is optional. Without the trailing return type, the return type of the underlying function is effectively auto, and it is deduced from the type of the expressions in the body's return statements.
This is depicted in the example 4.
Lambda in action
Following are some examples where lambdas can be used in stl and concurrency.
This is depicted in the example 5.
The following example demonstrates usage of recursive lambda expression. note auto cannot be used to store lambda expression.
function<void(int)> fib = [t1=0, t2 = 1,t=0] (int n) mutable { if (n>0) { cout << t1 << " "; t = t2; t2 += t1; t1 = t; fib(n-1); } else cout << " = " << t1 ; };fib(5); //0 1 1 2 3 = 7fib(9); //0 1 1 2 3 5 8 13 21 = 54
predefined function objects
Following predefined function objects are defined in the std. library to perform bitwise, relational, logical and arithmetic operations.
Name | Description | Example |
---|---|---|
bit_and | Bitwise AND | bit_and{}(0xff,0x0c); //returns 0x0c |
bit_or | Bitwise OR | bit_or{}(0xf0,0x0c); //returns 0xfc |
bit_xor | Bitwise XOR | bit_xor{}(0xff,0x0c); //returns 0xf3 |
not_equal_to | non-equality comparison | not_equal_to{}(10,2); //returns false |
equal_to | quality comparison | equal_to{}(10,2); //returns false |
greater | greater-than inequality comparison | greater{}(10,2); //returns true |
greater_equal | greater-than-or-equal-to comparison | greater_equal{}(10,2); //returns true |
less | less-than inequality comparison | less{}(10,2); //returns false |
less_equal | less-than-or-equal-to comparison | less_equal{}(10,2); //returns false |
logical_and | Logical AND | logical_and{}(true,false); //returns false |
logical_not | Logical NOT | logical_not{}(true); //returns false |
logical_or | Logical OR | logical_or{}(true,false); //returns true |
minus | Subtraction | minus{}(10,2); //returns 8 |
modulus | Modulus | modulus{}(10,2); //returns 0 |
multiplies | Multiplication | multiplies{}(10,2); //returns 20 |
negate | Negative | negate{}(10); //returns -10 |
divides | Division | divides{}(10,2); //returns 5 |
plus | Addition | plus{}(10,2); //returns 12 |
This is depicted in this example 6. :
function pointers
function pointers are pointers to functions that can be defined as variables. function pointer can be defined for "C" type free functions as shown below.
void print(int) {} void (*fp)(int) = &print; fp(8); //calls print(8);
function pointers can be also defined for members of a class such as fields and functions. However it's not possible to directly assign from an instance of a class. Therefore operators ->* and .* are used exclusively to call functions or access fields of a class as shown below. Notice that function call requires extra parenthesis.
struct Msg { char buf[50]; void print(){cout << buf;}; } m{}; char (Msg::*buf)[50] = &Msg::buf; void (Msg::*fp)() = &Msg::print; strcpy(m.*buf,"hello, world"); //same as m.buf or (&m)->buf (m.*fp)(); // same as m.print() or ((&m)->*fp)()
wrapper classes
As seen from the above, function pointers can be tedious to use. Wrapper classes provide alternative. These are classes that hold an object and have an interface similar to that object, but adding or changing some of its features:
function
Class that can wrap any kind of callable element (such as functions and function objects) into a copyable object, and whose type depends solely on its call signature (and not on the callable element type itself).
The syntax is as shown below:
template< class > class function; /* undefined */ template< class R, class... Args > class function<R(Args...)>;
An object of a function class instantiation can wrap any of the following kinds of callable objects: a function, a function pointer, a pointer to member, or any kind of function object (i.e., an object whose class defines operator(), including closures).
A decay copy of the wrapped callable object is stored internally by the object, which becomes the function's target member. The specific type of this target callable object is not needed in order to instantiate the function wrapper class; only its call signature.
reference_wrapper
reference_wrapper is a class template that creates a wrapper around a reference to object or reference to function of type T. Instances of reference_wrapper are objects but they are implicitly convertible to T&, so that they can be used as arguments with the functions that take the underlying type by reference.
The syntax is as shown below:
template <class T> class reference_wrapper;
The example 8 following lists the usage:
int a(10),b(20),c(30); // array of int& reference_wrapper<int> refs[] = {a,b,c};// vector of int& vector<reference_wrapper<int>> vec {a,b,c};
placeholder namespace
This namespace defines an unspecified number of objects: _1, _2, _3,..., which are used to specify placeholders in calls to function bind. When the function object returned by bind is called, an argument with placeholder::_1 is replaced by the first argument in the call, placeholder::_2 is replaced by the second argument in the call, and so on.
is_placeholder trait class can be used to check if the argument is a placeholder type or not.
functions
These functions are defined to help with reference and function wrapper classes discussed above.
ref and cref
Function templates ref and cref are helper functions that generate an object of type reference_wrapper, using template argument deduction to determine the template argument of the result.
The example 9 following lists the usage:
void f(int& n1, int& n2, const int& n3) {} // Example int n1 = 1, n2 = 2, n3 = 3; function<void()> bound_f = std::bind(f, n1, std::ref(n2), std::cref(n3));n1 = 10; n2 = 11; n3 = 12; bound_f(); //calls f(1, 11, 12)
mem_fn
This function converts member function to a functor whose functional call invokes the member function pointed by pm.
The syntax is as shown below:
template <class Ret, class T>/* unspecified */ mem_fn (Ret T::* pm);
ts functional call takes as first argument an object of type T (or a reference or a pointer to it) and, as additional arguments, the arguments taken by pm (if any). The effect of such a call with fn as first argument are the same as calling fn.*pm (or (*fn).*pm if fn is a pointer), forwarding any additional arguments.
The example 10 following lists the usage:
bind
Returns a function object based on fn, but with its arguments bound to args. The syntax is as shown below.
template <class Fn, class... Args>/* unspecified */ bind (Fn&& fn, Args&&... args); template <class Ret, class Fn, class... Args>/* unspecified */ bind (Fn&& fn, Args&&... args);
- fn is a function object, pointer to function or pointer to member.
- args is a list of arguments. Each argument may either be bound to a value or be a placeholder. If bound to a value, calling the returned function object will always use that value as argument otherwise the value passed in the call will be forwarded at runtime.
- ret A function object that, when called, calls fn with its arguments bound to args.
Summary of Examples
Name | Description | github | wandbox |
---|---|---|---|
Example | Generic Functor | source output | source + output |
Example 2 | Lambda - Capture and mutable | source output | source + output |
Example 3 | Lambda - Parameters | source output | source + output |
Example 4 | Lambda - Trailing return type | source output | source + output |
Example 5 | Lambda - in Action | source output | source + output |
Example 6 | Predefined functors | source output | source + output |
Example 7 | function | source output | source + output |
Example 8 | reference_wrapper | source output | source + output |
Example 9 | ref and cref | source output | source + output |
Example 10 | mem_fn | source output | source + output |
Example 11 | bind - simple | source output | source + output |
Example 12 | bind - complex | source output | source + output |
No comments:
Post a Comment