C _Functions - RicoJia/notes GitHub Wiki
========================================================================
========================================================================
- A functor is an object that acts like a function, using the overloaded operator().
struct greaters{ greaters(){} operator()(int a, int b){return a > b;} } struct increment{ private: int a public: increment(int a){a = a;} operator()(int b){return b+a} } std::transform(arr, arr+n, arr, increment(a)); //equivalent to calling increment(a) first. std::make_heap(arr, arr+n, greaters()); //calls constructor first, then inside the function, it will call the overloaded().
- Above is a cool use of functor, too
========================================================================
======================================================================== 0. An anonymous function object (or functor).
-
Basic structure is
[capture](inputs)->return_type`{ return something } //Capture is capturing a variable in the current scope (inside a vector is not a scope). capture a copy[=], a reference[&], a specific variable [var] // return type is optional.
- An example is
auto get_point = [range_vec](unsigned int index)-> rigid2d::Vector2D { double theta = rigid2d::PI*2.0* static_cast<double>(index)/360.0; double range = range_vec[index]; double x = range * sin(theta); double y = range * cos(theta); rigid2d::Vector2D point (x,y); return point; }; // you only get a functor here
- capture by value:
double data; auto func = [=](int i){}; //default auto func = [data](){}
-
Capture by reference
[=, &i]{}; // OK: by-copy capture, except i is captured by reference
- lambda reference capture: the reference will be copied inside the closure.
auto lam = [&]{}; std::thread th1 (lam); // the reference is copied over too.
- capture by reference is non-const.
- lambda reference capture: the reference will be copied inside the closure.
-
Call a lambda in a ctor, or anywhere you want:
// example 1 const int i = []{return 1;}(); //note the () //example 2 class Foo{ int i_; Foo(): i_([]{return 1;}()){} };
-
lambda expression can be used as predicate for std::find
auto search_result = std::find_if(this->open_list.begin(), this->open_list.end(), [&id](const A_star_node& node){return id == node.prm_vertex.id; });
-
Lambda expression's return type is required when you have recursive calls within itself.
- You also need
#include <functional> std::function <bool (args)>
- You also need
-
Lambda Expression Theory
- In compilation, closure class is created from an lambda expression
- In runtime, a closure object is created as an instance of closure class. It can be copied. can we capture? Yes, that's how a lambda works, Cpp insights
int i = 1; [&i](int a){ printf("%d\n", a);} // equivalent to: int i = 1; class __lambda_12_16 { public: inline /*constexpr */ void operator()(int a) const { printf("%d\n", a); } private: int & i; public: __lambda_12_16(int & _i) : i{_i} {} }; visit(vec, __lambda_12_16{i});
-
Empty parameter list () can be omitted, if there's no mutable, constexpr, noexcept, trailing types
auto func = [vec]{return 1;}; //OK auto func = [vec]mutable{return 1;}; //wrong
-
Mutable Lambdas: the closure by default, sets capture-by-copy params reference-to-const, i.e, they're not modifiable
- Because operator () inside the closure class is const, so the params are consts
std::vector<int> vec= {1,2,3}; auto func = [vec]{vec[1]=1000;}; //Wrong, vec is automatically const auto func = [vec]()mutable{vec[1]=1000;}; //compiles
- Code from the body will be transferred into the operator() function.
-
Mutable Lambdas are "stateful" if you copy a lambda, i.e, the params are stored as a member variable, and change everytime we call it
int x = 0; auto foo = [x] () mutable { /* "x" cannot be modified without keyword mutable. */ x++; //OK return x; }; // call foo std::cout << foo() << " "; //get 1 // assign foo to bar, they are both objects!! auto bar = foo; //copied 1 // call foo again std::cout << foo() << " "; // get 2 // call bar std::cout << bar() << " "; //get 2
-
Move an object into a closure
- In C++14, there's an easy way to do it
- Because in C++11, you cannot capture the result of an expression, but in C++14 you can, it's now called "generalized lambda capture"
auto func = [pw = std::make_unique<Widget>()]{pw -> foo = 100; }; //OK, pw is a move-constructed copy auto func = [pw = std::move(widget_ptr)]{}; //OK
- In C++11, you have to find substitutes
- Functor
class Functor{ public: using Ptr = std::unique_ptr<Widget>; explicit Functor(Ptr&& ptr) : ptr_(std::move(ptr)){} //you need std::move to transfer ownership bool operator() ()const{return true;} private: Ptr ptr_; }; auto func = Functor(std::make_unique<Widget>());
- std::bind objects
std::vector<int> data; // 1. the second argument of std::bind is std::move // 2. Because internally, std::bind will create a copy of all arguments, and that object will be created using its move ctor // 3. That object is an lvalue inside std::bind, so use const std::vector<int>&, or std::vector<int>& auto func = std::bind([](const std::vector<int>& data){......}, std::move(data));
- In C++14, there's an easy way to do it
-
C++14 has "generic lambda" (I think this is the only place you can see auto as an argument)
- Basics
auto some_func = [&](auto a, auto b){}; //valid auto some_func = [](auto a, int b){}; //params don't have to be all auto
- Internally, some_func becomes
class CompilerGeneratedLambda{ template<typename T, typename B> auto operator()(T a, B b) const{ } // Don't forget the const, by default operator () of lambdas are const functions }
- Universal reference
auto some_func1 = [](int&& x){}; auto some_func2 = [](auto&& x){}; int i = 1; some_func1(i); //Error, needs rvalue reference some_func2(i); //Fine
- Variadic Params
// for auto&& and iny&&, many others auto some_func1 = [](auto&& ...args){ return f(std::forward<decltype(args)>(args)...); }; // for auto&& only auto some_func2 = [](auto&& ... args){ return f(decltype(args)(args)); //? }
- Basics
-
Lambda vs std::bind
- Lambda is more concise (see cautions in std::bind )
- For example, you need a lot of bind to make sure args were evaluated when they're called, not when they they're passed in.
- You need type casting to disambiguate overloaded functions
- Functions inside a Lambda is inlined in its closure class, while in
std::bind
, they're function ptrs, which might be slower - std::bind by default creates copies to params passed by it
- Lambda is more concise (see cautions in std::bind )
-
lambda doesn't need to capture static member variables
class Foo{ public: static void foo(){ auto func = []{f = 666;}; func(); } static int f; }; int Foo::f(999);
- Pass by reference: You gotta make sure the reference will live longer than the closure
std::vector<std::function<int(int)>>; void addDivisor(){ int divisor = 32; filters.emplace_back([&](int value){return value/divisor;}); } // Error: divisor lives shorter than the closure
- So make sure you use the closure right after the references
- Good practice: write out all the references.
std::all_of(vec.begin(), vec.end(), [](int i){return i > 0;}); std::all_of(vec.begin(), vec.end(), [](const auto& i){return i > 0;})
- Pass by value:
- You should not copy raw pointer, use a unique ptr. Otherwise, there might be double freeing/dangling ptr
- static/global variables are accessible to any lambdas
- static/global variables are known during compile time, so their memory is known and won't change
- pass by reference/copy means "to put the reference/copy into the function".
- So there's no need to put a reference/copy to static/global variables
- So pass by value is not immuned to accessing static variables
void addDivisor(){ static int divisor std::vector<std::function<int(int)>> lambda_vec; lambda_vec.emplace_back(){ [=](int value){return value/divisor;} } ++divisor; // You think you've passed a copy of divisor? Wrong! }
- You cannot copy a member variable directly
class Foo{ int divisor; void addDivisor(){ std::vector<std::function<int(int)>> lambda_vec; lambda_vec.emplace_back(){ [divisor](int i){return value/divisor;} //ERROR: divisor is not a local variable [=](int i){return value/divisor;} //This works, because = will copy this pointer, then divisor is implicitly expanded as this -> divisor } } };
- but even using = can possibly put a dangling pointer into the vector.
std::vector<std::function<int(int)>> lambda_vec; int main(){ Foo* foo_ptr = new Foo(); foo_ptr -> addDivisor(); delete foo_ptr; //From now on, lambda_vec has dangling ptr in it! }
-
The simplest and safest thing to do, is to make a copy of the member variable, then push it into the function
std::vector<std::function<int(int)>> lambda_vec; void Foo::addDivisor(){ auto divisor_copy = divisor; lambda_vec.emplace_back( [divisor_copy](int value){return value/divisor;} //ERROR: divisor is not a local variable }); //C++11 way lambda_vec.emplace_back( [divisor = divisor](int value){return value/divisor} ); }
- but even using = can possibly put a dangling pointer into the vector.
- Pass by value might be better than pass by reference
- Scenario: two functions for lvalue, rvalue std::string are kinda redundant.
- If they're inlined, that's fine, because inline functions are eventually not actual function calls in machine code
- If they're not inlined, they will be two functions in object code, that'd be kinda redundant!!
- Solution 1: universal reference
template <typename T> class Foo{ void Bar(T&& b); // here T should either be std::string&& or std::string&& }
- Cons:
- May stay only in header file because of the template
- When you pass in a string literal, a temp obj will be created for std::string, then you move the temp obj, and finally you destroy it.
- So functions taking in universal reference will be more expensive with input args
- Perfect forwarding can fail:
- Passing in bitfields, cuz bits cannot be referenced. On hardware level, ref and pointer are the samething.
- Passing in Templated function without casting its type.
- 0 or NULL as they will be deduced to int instead of ptr type
- if type deduction fails, perfect fowarding fails.
- Braced Initializer
- you can explicitly create such an object using auto
- Specific to
std::string
-
char 16_t[]
cannot be directly converted to std::string - If
char 16_t[]
is passed in as an argument and perfect-forwarded to std::string, error msg is ugly
-
- Solution 2: pass by value
Foo::Bar(std::string new_name); Bar(std::move(some_name));
- Pros:
- new_name is completely independent
- You can use std::move for a name that's not used any more.
- Cons:
-
One more move if we pass in
std::move
(but if this move is not expensive, we can totally use it!!)- the cost is: move-construct temp obj, use it just as passing in lvalue ref and rvalue ref, finally destroy it
- cost for lvalue ref, rvalue ref: 0.
- Consider this only for copyable objects. Non-copyable objects will likely be movable only, which is better for rvalue-refeence + std::move
-
One more move if we pass in
- Cautions:
- You construct object by 1. ctor, 2. assignemnt =
-
std::string
move assignment (likestd::vector
) will steal the ptr to the underlying char[] - copy ctor won't steal the ptr, instead it will copy chars.
- the old char[] will be deallocated, if it's shorter than the new string.
- This can add up, so by default DO NOT USE PASS BY VALUE
- You might have some "slicing issues" with pass-by reference (no inheritance enabled)
- Pros:
- Scenario: two functions for lvalue, rvalue std::string are kinda redundant.
========================================================================
========================================================================
-
Motivation
- std::function can store any "callable objects", (class with overloaded(), lambda,etc.), aims to replace function pointer.
-
Use of std::function
- std::function(void(output)) is common practice
- std::function can be used as a left value
#include <functional> int func (int) {} class Bar{public: int operator()(int a){return a; }};//this is a functor int main() { Bar bar; std::function<int(int)> foo = func; foo = bar; }
- std::function can be used to represent static, non static functions, after binding.
int(int) func; class Lol{ static int (int a) static_lol; int (int a) non_static_lol; } int main(){ std::function<int(int)> foo1 = func std::function(int<int>) foo3 = Lol::static_lol; Lol lol; std::function<int(int)> foo2 = std::bind(&Lol::non_static_lol, &lol, std::placeholders::_1); func1(1) }
-
const std::function
just means this object doesn't point to other objs.std::function <void const...>
is actually pointing to a const function.
-
Drawback of
std::function
- throws an exception.
- std::function is slower, and takes up more space, because it the object itself takes up fixed amount of memory on stack. If memory is not enough, it will go to heap
-
std::function
requires the callable to be copy constructible- non-copy constructible functions can be a move-only lambda, which can be stored in
std::unique_function
- This can happen when you have a lambda that captures a
std::promise
, std::promise is not copyable, so this lambda is not copy constructible. - see this nice article
- non-copy constructible functions can be a move-only lambda, which can be stored in
-
check if
std::function
has a valid functionvoid checkFunc( std::function<void()> &func ) { // Use operator bool to determine if callable target is available. if( func ) { } } // or if (func != nullptr){ }
========================================================================
========================================================================
-
Function pointer vs
std::function
- function pointer is 16 bytes, while a regular pointer is only 8 bytes.
- Function pointer points to data, not to code, so we don't need to free that memory
-
Return type of a function pointer should have *
- Also, if we remove (), it will become a function that returns void*.
void (*func_ptr)(double) = &some_void_func; void (*func_ptr)(double) = some_void_func; //the function name = the function's address
- There's also a simpler version of function pointer
int pf(int);
- Invoking a function thru function pointer:
(*func_ptr)(10); func_ptr(10); //the function name = the function's address
-
pointer to member function vs functor vs std::function:
- function pointer to member function and const member function
class Foo{ bool f1(int i); void func() const {printf("hello");} } // one: pointer to const member function using int (Foo::*func)(int) const = &Foo::func; //of course to call it properly you have to do std::bind(foo_func, &foo_obj) int main(){ bool (Foo:*mfn)(int); mfn = &Foo:f1; //in c++, member functions belong to the class, not a specific object. Foo foo; (foo.*mfn)(11); //Pay attention to the syntax here!! }
- Functor for member function, using
std::mem_fn
, which generates a wrapper that can call a member function of the parent object.
#include <functional> struct Foo{ void foo(int i){} }; int main(){ Foo f; auto foo_wrapper = std::mem_fn(&Foo::foo); foo_wrapper(f, 20); }
- Cons: you have to know the exact type of the object. If the object type is different, even if your function is of the same type, you're done.
- USE an std::function instead
using function_type = std::function<double(int, float, double)>; std::vector<function_type> bindings; // var #1 - you can use simple function function_type var1 = foo_fn; bindings.push_back(var1); // var #2 - you can use member function function_type var2 = std::bind(&foo_struct::foo_fn, fs, _1, _2, _3); bindings.push_back(var2);
========================================================================
========================================================================
-
generate a function object
- succeeds std::bind1st, std::bind2nd
- IF you can use lambda, don't use std::bind
-
bind can be used on: function object, a pointer to function, or even a pointer to member, and will return an object of the same type.
#include <functional> int func1(int){} class Lol{ public: int l; void non_static_lol(int i){cout<<i<<endl;}; }; int main() { //bind to function object std::bind(func1, std::placeholders::_1); // bind to a member function Lol lol; auto func2 = std::bind(&Lol::non_static_lol, &lol, std::placeholders::_1); //Note that the first parameter should be an object for the class, so you get x.non_static_lol(); ALSO IMPORTANT: don't use object->func for bind!! func2(4); }
-
std::bind(&func, 1)
always passes 1 into the function. - std::placeholders::_1, _2 ..., these are the Nth argument.
-
-
Nice Usage
- passes in func1 as an argument for Foo, and func1 doesn't have to be evaluated when being passed in, but when Foo is invoked
Foo(int); int func1(return CurrentTime); //currenttime in seconds //without std::bind Foo(func1()); //CurrentTime is evaluated when being passed in //std::bind auto Foo_bound = std::bind(Foo, std::bind(func1)); //You put func1 there, but not evaluated yet. Foo_bound(); // now currenttime is being passed in.
- passes in func1 as an argument for Foo, and func1 doesn't have to be evaluated when being passed in, but when Foo is invoked
-
Cautions
-
Internally, std::bind will create a copy of all arguments. Therefore, move/copy ctor might be invoked
Foo f; int i = 3; std::bind(func1, std::move(i)); // move ctor is used inside std::bind to create a copy of the param std::bind(func1, i); // copy ctor is used internally
- VERY IMPORTANT, you should use std::ref to bind the function from the same object, otherwise, copy ctor is called by std::bind!!!. std::ref is also in . By default, std::bind has const operator() for its params
std::bind(&Foo::func, std::ref(obj));
- **You need type casting to disambiguate overloaded functions** ```cpp void Foo(Time t); void Foo(Time t, double i); auto Bar = std::bind(Foo, std::bind(std::plus<>(), std::chrono::steady_clock::now(), 1h), std::placeholders::_1); // ERROR: std::bind doesn't resolve overloaded functions, so there's ambiguity here auto Bar = std::bind(static_cast<void(*)(Time, double)>(Foo), std::bind(std::plus<>(), std::chrono::steady_clock::now(), 1h), std::placeholders::_1); // std::bind doesn't resolve overloaded functions, so there's ambiguity here ```
- std::bind internally has a function pointer that calls the bound function, which might be slower than lambda (inline function)
Bar(123.4); // calls the function to Foo // Lambda auto Bar_lambda = [](double i){Foo(i);}; //Foo is inline, which is faster
- Bind to class member:
Foo::foo(){ std::bind(&Foo::bar, this, std::placeholders::_1); } void Foo::bar(int){ }
- Note: this pointer must have been constructed, so you can't do this in initializer.
-
Internally, std::bind will create a copy of all arguments. Therefore, move/copy ctor might be invoked