C++ Closures: Functors, Lamdas and std::function

Discussion in 'C++' started by BiplabKamal, Jun 3, 2016.

  1. BiplabKamal

    BiplabKamal Member

    Joined:
    Dec 30, 2015
    Messages:
    37
    Likes Received:
    7
    Trophy Points:
    8
    Occupation:
    Software Engineer
    Location:
    Kolkata

    Objects vs Closures



    Closures might not be a familiar term for most of the C++ programmers. C++ programmers are very much familiar with class, object and function. Instances of classes are first-class objects in C++ but functions are not first-class entity in C++. An object or function to be first-class, you should be able to create them, store them in data structures, pass them to a function as parameters, return them from a function and create copies of them. You may think that you can do all those actions with function by using function pointer. But function pointer is just an address of the function. You can not create a function at run time or create a copy of it. A first-class function can have a state, which is not possible for normal function though it is partially possible with static variable. A first-class function is called closure or function object. We will use function object and closure interchangeably to mean same thing. The main difference between class object and function object is that function object can be used as a function. This make the closures callable objects. If you have an object named ‘MyObject’ and a closure named ‘MyClosure’ you can call MyClosure(); but cannot call MyObject().

    Class vs Closure



    The type of an object is called class, similarly type of closure is called closure class. Class instances are called objects and closure instances are called function objects. Class encapsulate data and functionality. Closure class encapsulate function and it’s internal state. Closures are also called callable objects. C++ allows programmers to create closure classes in three ways: Functor, Lamda and std::function

    Functors: Function objects in C++



    Functor class is a normal class which overloads the function call operator (). Function call operator can take any number of arguments and can return anything while all other operators have fixed number of arguments. Invoking the function call operator looks like using the object as a function. We will use “calling” the object to mean invoking the operator(). Here is an example of functor class and use of it:

    Code:
    #include<iostream>
    using namespace std;
    class MyFunctorClass
    {
    private:
        const float pi = 3.14159;
        float r{ 0 };
        float area{ 0 };
    public:
        MyFunctorClass(float radius) :r{ radius }
        {
            area = pi*r*r;
        }
        float operator()()
        {
            return area;
        }
    };
    
    int main()
    {
        MyFunctorClass circle_5(5), circle_10(10);
        cout << "Area of the circle with radius 5 cm is " << circle_5()<<" sqr cm" << endl;
        cout << "Area of the circle with radius 10 cm is " << circle_10()<<" sqr cm" << endl;
    
        return 0;
    }
    
    In the above code MyFunctorClass is a functor class. Two instances are created. Each instance is giving only the area for a circle with a particular radius. This radius is passed while creating the function object. Calling circle_5() is equivalent to calling circle_5.operator()().

    We could have have pass the radius as the argument to the function call operator instead of constructor argument. In that case single function object will give area for different size circles. Following example has two functor classes, one for creating function object to compare two numbers and another for creating function objects to display a number to the console. Function objects of both type are passed as arguments to another function:
    Code:
    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    //Functor class for comparing 2 integer elements
    class Compare {
    public:
        Compare(bool ascending):Asending{ascending}{}
        // Overloading function call operator which take 2 int argument and return true or false 
        bool operator()(int first, int second) const 
        {
            if (Asending)
                return first < second;// Will sort in ascending order
            else
                return first > second; // Will sort in descending order
        }
    private:
        bool Asending{ true }; //Flag for ordering
    };
    //Functor class for displaying a number
    class Show
    {
    public:
        // Overloading function call operator which take 1 int argument print it
        void operator()(int element)
        {
            cout << element << " ";
        }
    
    };
    
    int main() {
        vector<int> v;
        for (int i = 1; i<10; ++i) {
            v.push_back(i);
        }
        cout << "Intial elements:" << endl;
        Show show_element;
        std::for_each(v.begin(), v.end(), show_element);
        cout << endl;
    
        Compare Descending(false);
        Compare Ascending(true);
    
        cout << "Sorted descending elements:" << endl;
        std::sort(v.begin(), v.end(), Descending);
        std::for_each(v.begin(), v.end(), show_element);
        cout << endl;
    
        cout << "Sorted ascending elements:" << endl;
        std::sort(v.begin(), v.end(), Ascending);
        std::for_each(v.begin(), v.end(), show_element);
        cout << endl;
        return 0;
    }
    
    Output of the above program:
    Code:
    Intial elements:
    1 2 3 4 5 6 7 8 9
    Sorted descending elements:
    9 8 7 6 5 4 3 2 1
    Sorted ascending elements:
    1 2 3 4 5 6 7 8 9
    
    Functor objects can be copied or returned from a function.

    Lamda Expression



    Lamda expression is a new tool in C++11 to create unnamed function object or closure. Defining a lamda expression is similar to a function without the function name. In addition lamda can use variables from the scoping environment. Variables to be captured from the scoping environment are specified withing square bracket ([]) at the beginning of the lamda header. We will not discuss the details of lamda expression here. You can go through the article about the Lamda expression. Following example will use lamda expression instead of functors and implement the same functionality done in the previous example of sorting and displaying of vector elements.

    Code:
    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    int main() {
        vector<int> v;
        for (int i = 1; i<10; ++i) {
            v.push_back(i);
        }
        cout << "Intial elements:" << endl;
        //Creating lamda expression
        auto show_element = [](int element) {cout << element << " ";};
        // Passing the lamda as function argument
        std::for_each(v.begin(), v.end(), show_element);
        cout << endl;
        bool Ascending{ false };
        //Creating lamda expression
        auto Compare = [&Ascending](int first, int second)
        {
            if (Ascending)
                return first < second;// Will sort in ascending order
            else
                return first > second; // Will sort in descending order
        };
        
        cout << "Sorted descending elements:" << endl;
        std::sort(v.begin(), v.end(), Compare);
        std::for_each(v.begin(), v.end(), show_element);
        cout << endl;
    
        Ascending = true;
        cout << "Sorted ascending elements:" << endl;
        std::sort(v.begin(), v.end(), Compare);
        std::for_each(v.begin(), v.end(), show_element);
        cout << endl;
        return 0;
    }
    
    Though the syntax of lamda and functor are different they create same closure internally. Captured variables become the private members of the closure class, and lamda body becomes the body of the operator().

    STL class std::function



    You can create function object by using a functor class or lamda expression. Both have their advantages over the another in different scenarios. Normally lamda expressions are used when the function is small and simple enough to create on the fly. Otherwise use functor class. If you want to pass function as a parameter you can use function pointer or function objects like functor or lamda. But how do you declare a function which takes a function pointer or functor or lamda as a parameter? Look at the following example of a function with argument type of function pointer.

    Code:
    #include <iostream>
    using namespace std;
    // Functor
    class MyFunctor
    {
    public:
        void operator()()
        {
            cout << "MyFunctor called" << endl;
        }
    };
    //Function
    void MyFn()
    {
        cout << "MyFn() called: " << endl;
    }
    void AcceptFunctionPointer(void(*f)())
    {
        f();
    }
    
    int main() {
        int x = 10;
        auto lamdawithcapture = [x]() {cout << "Lamda called, capture value is "<<x << endl;};
        auto lamdawithoutcapture = []() {cout << "Lamda without captured value called" << endl;};
        //Passing a function pointer
        AcceptFunctionPointer(MyFn);
        //Passing functor
        //AcceptFunctionPointer(MyFunctor()); // Error: Argument type mismatch
        //Passing a lamda without capture
        AcceptFunctionPointer(lamdawithoutcapture);
        //Passing lamda with capture
        //AcceptFunctionPointer(lamdawithcapture);// Error: Argument type mismatch
    
        
        return 0;
    }
    
    If you see the function AcceptFunctionPointer() in the above program that it cannot accept all types of function objects. It can accept only a function pointer or a lamda without captured variables. So how to declare a function which can accept other type of function objects? C++11 has provide a template functor class ‘function’ in the namespace ‘std’ which can wrap any type of function object including function pointer. This template function is declared in the header <functional>. Following code show how the std::function class helps to declare a function which can accept function pointer, functor and lamada:

    Code:
    #include <iostream>
    #include<functional>
    using namespace std;
    // Functor
    class MyFunctor
    {
    public:
        void operator()()
        {
            cout << "MyFunctor called" << endl;
        }
    };
    //Function
    void MyFn()
    {
        cout << "MyFn() called: " << endl;
    }
    void AcceptFunctionPointer(std::function<void()> f)
    {
        f();
    }
    int main() {
        int x = 10;
        auto lamdawithcapture = [x]() {cout << "Lamda called, capture value is "<<x << endl;};
        auto lamdawithoutcapture = []() {cout << "Lamda without captured value called" << endl;};
        //Passing a function pointer
        AcceptFunctionPointer(MyFn);
        //Passing functor
        AcceptFunctionPointer(MyFunctor());
        //Passing a lamda without capture
        AcceptFunctionPointer(lamdawithoutcapture);
        //Passing lamda with capture
        AcceptFunctionPointer(lamdawithcapture);
    
        
        return 0;
    }
    
    The output of the program:
    Code:
    MyFn() called:
    MyFunctor called
    Lamda without captured value called
    Lamda called, capture value is 10
    
    So the template class std:function provides a functor type, so that function pointer, functor and lamda expression can be converted to it. You can treat all kind of function objects similar way. In fact you can think all as closures which have only function operator () as the public function and some private constant members. Constant member functions are alternative to function operator arguments. Constant members reduce the number of function operator arguments but limit the function input variables hence less generic. Standard library template function std:bind helps to convert input arguments to private constant members discussed below.

    std::bind



    Standard Library also provide a template function std::bind which create a std:function object from other function pointer, functor or lamadas by binding their arguments with the new function object. It allows you to create new type of function objects with binding the arguments of the source function. Binding can happen either with values or placeholders. Arguments bound with values need not to be passed while calling the new function object. The arguments bound with placeholders are needed to pass while invoking the new function object. Suppose you have functor as defined below-
    Code:
    //Functor class for comparing 3 integer elements
    class Compare2 {
    public:
        // Overloading function call operator which take 3 int argument and return true or false 
        bool operator()(int first, int second, bool Asending) const
        {
            if (Asending)
                return first < second;// Will sort in ascending order
            else
                return first > second; // Will sort in descending order
        }
    };
    
    The above functor calling need three parameter to pass. But the vector::sort function need a functor with first two arguments. So if we want to use the same functor we have to convert it to a new function object by binding the first two arguments with place holders and the third argument with value as shown below:

    std::bind(Compare2(), _1, _2, false) or
    std::bind(Compare2(), _1, _2, thrue)

    _1 and _2 are place holders for 1st and the 2nd argument respectively. The 3rd argument is bound with the object. So while invoking the object only place holders are to be passed. Place holders are declared in the namespace std::placeholders. Here is the full program:

    Code:
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include<functional>
    using namespace std;
    using namespace std::placeholders;
    
    //Functor class for comparing 3 integer elements
    class Compare2 {
    public:
        // Overloading function call operator which take 3 int argument and return true or false 
        bool operator()(int first, int second, bool Asending) const
        {
            if (Asending)
                return first < second;// Will sort in ascending order
            else
                return first > second; // Will sort in descending order
        }
    };
    //Functor class for displaying a number
    class Show
    {
    public:
        // Overloading function call operator which take 1 int argument print it
        void operator()(int element)
        {
            cout << element << " ";
        }
    
    };
    int main() {
        vector<int> v;
        for (int i = 1; i<10; ++i) {
            v.push_back(i);
        }
        cout << "Intial elements:" << endl;
        Show show_element;
        std::for_each(v.begin(), v.end(), show_element);
        cout << endl;
        cout << "Sorted descending elements:" << endl;
        std::sort(v.begin(), v.end(), std::bind(Compare2(), placeholders::_1, placeholders::_2, false));
        std::for_each(v.begin(), v.end(), show_element);
        cout << endl;
    
        cout << "Sorted ascending elements:" << endl;
        std::sort(v.begin(), v.end(), std::bind(Compare2(), _1, _2, true));
        std::for_each(v.begin(), v.end(), show_element);
        cout << endl;
        return 0;
    }
    
    The output of the program is:
    Code:
    Intial elements:
    1 2 3 4 5 6 7 8 9
    Sorted descending elements:
    9 8 7 6 5 4 3 2 1
    Sorted ascending elements:
    1 2 3 4 5 6 7 8 9
    
    You can also change the order of arguments passed to bind. Following program shows how order of arguments is changed and number of arguments are reduced in the target object call:
    Code:
    #include <functional>
    #include <string>
    #include <iostream>
    
    using namespace std;
    using namespace std::placeholders;
    
    void MyFunctiom(string s, int i, int j,int k)
    {
        cout << s <<" "<< i << " "<< j <<" "<< k << endl;
    }
    int main()
    {
        auto fn4 = bind(MyFunctiom, _1, _3, _2,_4);
        auto fn3 = bind(MyFunctiom,"HELLO", _3, _1, _2);//Changing the order of the arguments
        auto fn2 = bind(MyFunctiom, _2, _1, 2,3);//Changing the order of the arguments
        auto fn1 = bind(MyFunctiom, _1, 1, 2, 3);//Changing the order of the arguments
    
        fn1("Hi");
        fn2(1, "Hello");
        fn3(1,2,3);
        fn4("Hello",1, 2, 3);
        return 0;
    }
    
    The output is:
    Code:
    Hi 1 2 3
    Hello 1 2 3
    HELLO 3 1 2
    Hello 2 1 3
    
    You can also bind arguments of member functions like following code:
    Code:
    #include <functional>
    #include <iostream>
    using namespace std;
    using namespace std::placeholders;
    class Circle
    {
    private:
        const double pi = 3.14159;
    public:
        double GetArea(double radius)
        {
            return pi*radius*radius;
        }
    };
    int main()
    {
        Circle c;
        auto circlearea_5 = std::bind(&Circle::GetArea, c,5);
        cout << "Radius = 5, Area = " << circlearea_5()<<endl;
        return 0;
    }
    
    Note that in case of class member function in addition to function arguments the object of the class is also bound. That is why an extra parameter as the 2nd argument is passed to the bind function which is a class object.

    Traditional C++ programmers are used to function pointers but function object or closure may look like an alien to them. Modern C++ has given a lot of emphasis on function object (closure) and allows to do functional programming. It requires some practices with function objects to get the concept clear.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice