Functional C++
Slide ShowFunctional C++
section
Contents
C++
98- lambda functions
std::function
- signals and slots
C
-style
void qsort(void* pointer, size_t count, size_t size,
int (*comparator) (const void*, const void*));
int greater(const void* lhs, const void* rhs) {
const int* l = static_cast<const int*>(lhs);
const int* r = static_cast<const int*>(rhs);
return *r - *l;
}
// ...
std::qsort(numbers, 5, sizeof(int), &greater);
- does not work for non-POD
- needs a new function
C++'98
does not allow defining functions inside functions- the function does not allow to store state, unless it is global
- the function has to always be called indirectly through a pointer
- and will get called O(NlogN) times for sorting N numbers
C++
'98 style
void sort(T begin, T end, C comparator);
- works for everything that has
operator <
- needs a new function or functor
- you can store state
- the function can be inlined and optimized
template <typename T, typename P>
T partition(T begin, T end, P unary_predicate);
// partitions a range so that all X for which predicate(X) is true
// precede all Y for which predicate(Y) is false
template <typename T>
struct less_than_t
{
explicit less_than_t(T value)
: _value(value)
{}
bool operator()(T x) const {
return x < _value;
}
T _value;
};
template <typename T>
less_than_t<T> less_than(T x)
{
return less_than_t<T>(x);
}
std::partition(numbers, numbers + size, less_than(5));
- it is quite a lot of code
struct
can be defined inside a function- but such
struct
s can not be used for instantiating templates- the standard and
g++
say so - Visual Studio does not
- the standard and
- but such
C++
11 style
lambda
lambda
Anonymous functions that can be defined inside functions and can access variables defined outside the anonymous functions.
lambda expressions
[capture](parameters) -> return_type { function_body }
std::vector<int> v;
std::partition(v.begin(), v.end(), [](int x) {
return x % 2 == 0;
});
std::for_each(v.begin(), v.end(), [&](int x) {
std::cout << x << std::endl;
});
lambda captures
[]
- no variables are captured[x]
- x is captured by value[&x]
- x is captured by ref[&]
- any external variable is implicitly captured by ref[=]
- any external variable is implicitly captured by value[x, &y]
- x is captured by value, y - by reference
Captures
- pointers to lambda functions without captured variables are the same as pointer to non-member functions
this
can be captured only by value and only inside methods
Implementation of lambda functions
Most compilers generate a struct
with a special name and operator()
- no captures - might generate a non-member function
- captures - the captured variables are members of the
struct
The future for lambda functions
Generic lambdas
auto dbl = [](auto x) { return x + x; };
std::vector<int> vi;
std::transform(vi.begin(), vi.end(), vi.begin(), dbl);
std::vector<std::string> vs;
std::transform(vs.begin(), vs.end(), vs.begin(), dbl);
Initializers
std::vector<int> vi;
std::tranform(vi.begin(), vi.end(), vi.begin(),
[value = 42](auto x) {
return x + value;
});
Capture by move
auto ptr = std::make_unique<int>(42);
std::vector<int> vi;
std::tranform(vi.begin(), vi.end(), vi.begin(),
[ptr = std::move(ptr)](auto x) {
return x + *ptr;
});
Functions as first class citizen
std::vector<int> lengths;
std::vector<string> strings;
lengths.resize(strings.size());
std::transform(strings.begin(), strings.end(), lengths.begin(),
// ???
);
std::transform(strings.begin(), strings.end(), lengths.begin(),
std::string::length // ???
);
// No
Functions in C++
-
free functions
-
methods
free_function(a, 42);
object.method(42);
size_t get_length(const std::string& s) {
return s.length();
}
std::transform(strings.begin(), strings.end(), lengths.begin(),
get_length;
);
C++
98
std::transform(strings.begin(), strings.end(), lengths.begin(),
std::mem_fun_ref(&std::string::length);
);
std::mem_fun
- works only for methods without arguments or a single argument
- the object has to be supplied via a pointer
std::mem_fun_ref
works with a reference- DEPRECATED
Currying
std::bind1st
and std::bind2nd
Bind the first (second) argument of a function to a fixed value and return a function with one argument less.
DEPRECATED
std::vector<std::string> v;
std::transform(v.begin(), v.end(), v.begin(),
std::bind1st(std::plus<std::string>(), ";"));
// "x" -> ";x"
std::transform(v.begin(), v.end(), v.begin(),
std::bind2nd(std::plus<std::string>(), ";"));
// "x" -> "x;"
C++
11
std::mem_fn
Generalized version of std::mem_fun
.
- handles an arbitrary number of arguments
- the object can be passed by reference, plain or smart pointer.
typedef std::vector<std::shared_ptr<Players>> Players;
void Transform(Players& players, const Transform& t)
{
std::for_each(players.begin(), players.end(),
std::mem_fn(&Player::TransformWith));
}
std::bind
std::bind
- binding any argument of a function to a fixed value
- reordering of arguments
std::bind
- former
boost::bind
-boost
version is still boosted with extra features
template <typename Callback>
std::shared_ptr<Button> make_button(Callback callback);
struct Application {
void Quit();
};
Application app;
auto quit = make_button(std::bind(&Application::Quit, &app));
Be sure that the quit button will not be clicked after app
is destroyed.
std::shared_ptr<Application> app;
auto quit = make_button(std::bind(&Application::Quit, app));
Arguments
auto autosave = make_checkbox(std::bind(&Application::Autosave, app,
std::placeholders::_1));
C#
delegates in C++.
void Function(std::string s, int n, Person p) {
std::cout << p.name() << ": " << s;
while (n-- > 0)
std::cout << '!';
std::cout << std::endl;
}
Person p("Yoda");
auto mega_fun = std::bind(&Function, _2, _1, &p);
mega_fun(3, "The Answer is 42");
// Yoda: The answer is 42!!!
std::function
std::function
is a generic function object.
Functions in C++
Let me count the ways ...
Free functions
void free_function(int x) { std::cout << "free function: " << x << std::endl; }
// ...
void free_with_ud(const UserDefined& ud, int x)
{
std::cout << "free with ud " << &ud << ": " << x << std::endl;
}
// ...
free_function(42);
Functors
struct Functor
{
void operator()(int x) const
{
std::cout << "functor " << this << ": " << x << std::endl;
}
};
// ...
Functor functor;
functor(42);
Methods
struct UserDefined
{
void method(int x) const
{
std::cout << "method " << this << ": " << x << std::endl;
}
static void static_method(int x)
{
std::cout << "same as free but with access to internals" << std::endl;
}
};
// ...
UserDefined ud;
ud.method(42);
Pointer to functions
typedef void (*FreeFunctionPtr)(int);
FreeFunctionPtr ffp = &free_function;
ffp(42);
typedef void (UserDefined::*MethodPtr)(int) const;
MethodPtr mp = &UserDefined::method;
(ud.*mp)(42);
UserDefined* udp = &ud;
(udp->*mp)(42);
std::function
std::function<void(int)> generic = free_function;
generic(24);
generic = functor;
generic(2);
std::function
with types
std::function<void(const UserDefined&, int)> ud_function =
&UserDefined::method;
ud_function(ud, 42);
ud_function = &free_with_ud;
ud_function(ud, 42);
std::function std::bind
generic = std::bind(&UserDefined::method, ud, _1);
generic(4);
generic = std::bind(&UserDefined::method, std::ref(ud), _1);
generic(8);
Functions and tuples
Functions in C++11
template <typename T>
T plus(T lhs, T rhs) {
return lhs + rhs;
}
auto s = plus(2, 2);
Person p1;
Person p2;
auto s = plus(p1, p2)
struct Person
{
Household operator+(const Person&);
}
??? plus(Person lhs, Person rhs) {
return lhs + rhs;
}
auto plus(Person lhs, Person rhs) -> decltype(lhs + rhs)
{
return lhs + rhs;
}
Functions in C++14
??? plus(Person lhs, Person rhs)
{
return lhs + rhs;
}
auto plus(Person lhs, Person rhs)
{
return lhs + rhs;
}
- all returns must have the same type
- single return