C++ STL Functors

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

  1. BiplabKamal

    BiplabKamal Member

    Joined:
    Dec 30, 2015
    Messages:
    37
    Likes Received:
    7
    Trophy Points:
    8
    Occupation:
    Software Engineer
    Location:
    Kolkata
    STL includes a set of template classes that overload the function call operator (operator ()). Instances of those classes are called functors or function objects. Many algorithm functions in STL take those functors as input and apply them on the elements they operate. STL has two kinds of function objects:
    1. Unary Functor: Functor that can be called with one arguments
    2. Binary Functor: Functor that can be called with two arguments
    Among STL functors there is a group of function objects called predicate which take one or two arguments and return boolean value or object convertible to boolean value. The predicates which take one argument are called unary predicates and those who take two arguments are called binary predicates. STL functors are declared in the header <functional> and are part of namespace std. They are divided in following groups according their functionality:
    1. Functors for Arithmetic Operations: They are called for arithmetic operations like addition and subtraction
    2. Functors for Comparison Operations: They are called for comparing two values like equality or inequality.
    3. Functors for Logical Operations: They are called for logical operation like logical AND
    4. Functors for Bitwise Operations: They are called to perform bitwise operations like bitwise AND and bitwise OR.
    Following sections will highlight on individual functor class:

    Arithmetic Operations



    std::plus(Function object implementing x + y)

    template< class T = void >
    struct plus;
    template<>
    struct* plus<void>;


    Description: std::plus is a binary functor which take two operands and call the operator + for them. The default template argument is void and it is specialized for void type where it’s function operator deduce the argument type and return type from the arguments.

    Example:

    Code:
    #include <functional>
    #include <iostream>
    int main()
    {
        std::string s1 = "Hello ";
        const char* s2 = "World";
        std::plus<std::string> stringAdder3; // Adds two string objects 
        std::plus<> stringAdder1;// default type is void, so template specialization used
        std::plus<void> stringAdder2; // template specialization used
        
        std::cout << stringAdder1(s1,s2).c_str() << '\n';
        std::cout << stringAdder2(s1, s2).c_str() << '\n';
        std::cout << stringAdder3(s1, s2).c_str() << '\n';
    }
    
    Code:
    Hello World
    Hello World
    Hello World
    
    std::minus(Function object implementing x - y)

    template< class T = void >
    struct minus;
    template<>
    struct* minus<void>;

    Description: std::minus is a binary functor which takes two operands and calls the operator - for them. The default template argument is void and it is specialized for void type where it’s function operator deduce the argument type and return type from the arguments.

    Example:

    Code:
    #include <functional>
    #include <iostream>
    int main()
    {
        
        std::minus<int> intsubtructor1; // Subtract two integers
        std::minus<> intsubtructor2; // // default type is void, so specialization used
        std::minus<void> intsubtructor3; //  // template specialization used
        
        std::cout << intsubtructor1(20,10) << '\n';
        std::cout << intsubtructor2(200, 100) << '\n';
        std::cout << intsubtructor3(2000, 1000) << '\n';
    }
    
    Code:
    10
    100
    1000
    
    std::multiplies(Function object implementing x * y)

    template< class T = void >
    struct multiplies;
    template<>
    struct* multiplies<void>;

    Description: std::multiplies is a binary functor which take two operands and call the operator * for them. The default template argument is void and it is specialized for void type where it’s function operator deduce the argument type and return type from the arguments.

    Example:

    Code:
    #include <functional>
    #include <iostream>
    struct Money
    {
        int Doller;
        int Cent;
        Money& operator*(int m)
        {
            int d = (Cent*m) / 100;
            Cent = (Cent*m) % 100;
            Doller *= m;
            Doller += d;
            return *this;
            
        }
    
    };
    int main()
    {
        std::cout << std::multiplies<int>()(5,10) << '\n'; // Explicit type as the template argument
        std::cout << std::multiplies<void>()(5, 100) << '\n'; // template specialization used, type is auto deduced from the arguments
        Money m{ 3,40 };
        m=std::multiplies<>()(m, 4);
        std::cout << m.Doller <<" Doller "<<m.Cent<<" Cent" << '\n'; // Default template argument is void
    }
    
    Code:
    50
    500
    13 Doller 60 Cent
    
    std::divides(Function object implementing x / y)

    template< class T = void >
    struct divides;
    template<>
    struct* divides <void>;

    Description: std::divides is a binary functor which take two operands and call the operator / for them. The default template argument is void and it is specialized for void type where it’s function operator deduce the argument type and return type from the arguments.

    Example:
    Code:
    #include <functional>
    #include <iostream>
    struct Money
    {
        int Doller;
        int Cent;
        Money& operator/(int m)
        {
            int d = Doller%m;
            Doller = Doller / m;
            Cent += d * 100;
            Cent = Cent / m;
            return *this;
        }
    };
    int main()
    {
        std::cout << std::divides<int>()(10,5) << '\n'; // Explicit type as the template argument
        std::cout << std::divides<void>()(100, 5) << '\n'; // template speciliation used, type is auto deduced from the arguments
        Money m{ 5,40 };
        m=std::divides<void>()(m, 4);
        std::cout << m.Doller <<" Doller "<<m.Cent<<" Cent" << '\n'; // Default template argument is void
    }
    
    Code:
    2
    20
    1 Doller 35 Cent
    
    std::modulus(Function object implementing x % y)

    template< class T = void >
    struct modulus;
    template<>
    struct* modulus <void>;

    Description: std::modulus is a binary functor which take two operands and call the operator % for them. The default template argument is void and it is specialized for void type where it’s function operator deduce the argument type and return type from the arguments.

    Example:

    Code:
    #include <functional>
    #include <iostream>
    int main()
    {
        std::cout << std::modulus<int>()(10,7) << '\n'; // Explicit type as the template argument
        std::cout << std::modulus<void>()(100, 7) << '\n'; // template specialization used, type is auto deduced from the arguments
        std::cout << std::modulus<>()(100, 7) << '\n'; // template default argument is used, type is auto deduced from the arguments
        
    }
    
    Code:
    3
    2
    2
    
    std::negate(Function object implementing -x)

    template< class T = void >
    struct negate;
    template<>
    struct*negate <void>;

    Description: std::negate is a unary functor which take one operand and call the operator - for the argument of type T. The default template argument is void and it is specialized for void type where it’s function operator deduce the argument type and return type from the function operator argument.

    Example:

    Code:
    #include <functional>
    #include <iostream>
    struct Money
    {
        int Doller;
        int Cent;
        Money& operator-()
        {
            Doller = -Doller;
            Cent = -Cent;
            return *this;
        }
    };
    int main()
    {
        std::cout << std::negate<double>()(10.7) << '\n'; // Explicit type as the template argument
        std::cout << std::negate<void>()(100.7) << '\n'; // template specialization used, type is auto deduced from the arguments
        Money m{ 5,40 };
        m = -m;
        std::cout << m.Doller << " Doller " << m.Cent << " Cent" << '\n'; // Default template argument is void
    }
    
    Code:
    -10.7
    -100.7
    -5 Doller -40 Cent
    

    Comparison Operations



    std::<greater>(Function object implementing x > y)

    template< class T = void >
    struct greater;
    template<>
    struct*greater<void>;

    Description: This is a binary functor which takes two operands and call the operator > for the arguments of type T. It’s function operator checks if the first argument is greater than the second argument. The default template argument is void and it is specialized for void type where it’s function operator deduces the argument type and return type from the arguments passed to it function operator.

    Example:

    Code:
    #include <functional>
    #include <iostream>
    struct Money
    {
        int Dollar;
        int Cent;
        bool operator >(const Money& m)
        {
            if (Dollar > m.Dollar)
                return true;
            if (Dollar==m.Dollar)
                if (Cent > m.Cent)
                    return true;
            return false;
        }
    };
    int main()
    {
        Money m1{ 5,40 };
        Money m2{ 4, 50 };
        std::greater<> g;
        //std::greater<void> g; //Same as above
        //std::greater<Money> g // Same as above
        if (g(m1,m2))
        {
            std::cout << m1.Dollar << " Dollar " << m1.Cent << " Cent is greater than "<< m2.Dollar << " Dollar " << m2.Cent << " Cent" << '\n'; 
        }
        else
        {
            std::cout << m1.Dollar << " Dollar " << m1.Cent << " Cent is not greater than " << m2.Dollar << " Dollar " << m2.Cent << " Cent" << '\n';
        }
        
    }
    
    Code:
    5 Doller 40 Cent is greater than 4 Doller 50 Cent
    
    std::equal_to(Function object implementing x == y)

    template< class T = void >
    struct equal_to;
    template<>
    struct*equal_to<void>;

    Description: This is a binary functor which takes two operands and call the operator == for the arguments of type T. It’s function operator checks if the first argument is equal to the second argument. The default template argument is void and it is specialized for void type where it’s function operator deduces the argument type and return type from the argument passed to the function operator.

    Example:

    Code:
    #include <functional>
    #include <iostream>
    struct Money
    {
        int Dollar;
        int Cent;
        bool operator ==(const Money& m)
        {
        if (Dollar==m.Dollar && Cent == m.Cent)
        return true;
    
        return false;
        }
    };
    int main()
    {
        Money m1{ 5,40 };
        Money m2{ 5, 30 };
        std::equal_to<> e;
        //std::equal_to<void> e; //Same as above
        //std::equal_to<Money> e // Same as above
        if (e(m1,m2))
        {
            std::cout << m1.Dollar << " Dollar " << m1.Cent << " Cent is equal to "<< m2.Dollar << " Dollar " << m2.Cent << " Cent" << '\n'; 
        }
        else
        {
            std::cout << m1.Dollar << " Dollar " << m1.Cent << " Cent is not equal to " << m2.Dollar << " Dollar " << m2.Cent << " Cent" << '\n';
        }
        
    }
    
    Code:
    5 Dollar 40 Cent is not equal to 5 Dollar 30 Cent
    
    std::not_equal_to (Function object implementing x != y)

    template< class T = void >
    struct not_equal_to;
    template<>
    struct*not_equal_to<void>;

    Description: This is a binary functor which takes two operands and call the operator != for the arguments of type T. It’s function operator checks if the first argument is not equal to the second argument. The default template argument is void and it is specialized for void type where it’s function operator deduces the argument type and return type from the argument passed to the function operator.

    Example:
    Code:
    #include <functional>
    #include <iostream>
    struct Money
    {
        int Dollar;
        int Cent;
        bool operator !=(const Money& m)
        {
            if (Dollar!=m.Dollar || Cent != m.Cent)
            return true;
            return false;
        }
    };
    int main()
    {
        Money m1{ 5,40 };
        Money m2{ 5, 40 };
        std::not_equal_to<> ne;
        //std::not_equal_to<void> ne; //Same as above
        //std::not_equal_to<Money> ne // Same as above
        if (ne(m1,m2))
        {
            std::cout << m1.Dollar << " Dollar " << m1.Cent << " Cent is not equal to "<< m2.Dollar << " Dollar " << m2.Cent << " Cent" << '\n'; 
        }
        else
        {
            std::cout << m1.Dollar << " Dollar " << m1.Cent << " Cent is equal to " << m2.Dollar << " Dollar " << m2.Cent << " Cent" << '\n';
        }
    }
    
    Code:
    5 Dollar 40 Cent is equal to 5 Dollar 40 Cent
    
    Other comparison functors like std::less, std::less_equal and std::greater_equal are similar to above examples. Only the type of the arguments should support corresponding operators like <,<= and <= respectively.
    std::less: checks if the first argument is less than the second argument.
    std::less_equal: checks if the first argument is less than or equal to the second argument
    std:: greater_equal: checks if the first argument is greater than or equal to the second argument

    Logical Operations



    Logical operation functors std::logical_and and std::logical_or are binary functors which call operators && and || on the arguments. std::logical_not is a unary functor which calls the ! Operator on its argument. They are declared as:

    Logical and:

    template<*class*T*=*void*>
    struct*logical_and;
    template<>
    struct*logical_and<void>; //Specialization

    Logical or

    template<*class*T*=*void*>
    struct*logical_or;
    template<>
    struct*logical_or<void>; //Specialization

    Logical not:

    template<*class*T*=*void*>
    struct*logical_not;
    template<>
    struct*logical_not<void>; //Specialization

    The function operator members of the specialized templates deduce the type of template arguments and return types from the arguments passed to the function operators.

    Example:
    Code:
    #include <functional>
    #include <iostream>
    int main()
    {
        std::logical_and<int> la; // Argument type is int
        std::logical_or<> lo; // Argument type is void deduced
        std::logical_not<void> ln; //Argument type is void and deduced
    
        std::cout << la(10,0) << std::endl; // Logical AND operation of two integers. 
        std::cout << lo(10, 0) << std::endl; // Logical OR operation of two arguments, type deduction
        std::cout << ln(false) << std::endl; // Logical NOT operation of the argument, type deduction
    }
    
    Code:
    0
    1
    1
    

    Bit wise Operations



    There are four functors for bit-wise operations:

    std::bit_and – Binary function object which can perform bit-wise AND operation, x & y

    Declaration:

    template<class T = void >
    struct bit_and;
    template<>
    struct bit_and<void>; // Specialization

    std::bit_or – Binary function object which can perform bit-wise OR operation, x | y

    Declaration:

    template<class T = void >
    struct bit_or;
    template<>
    class bit_or<void>; // Specialization

    std::bit_xor – Binary function object which can perform bitwise xor operation, x ^ y

    Declaration:

    template<class T = void >
    struct bit_xor;
    template<>
    struct bit_xor<void>; // Specialization

    std::bit_not – Unary function object which can perform bit wise NOT operation, ~x

    Declaration:

    template<class T = void >
    struct bit_not;
    template<>
    struct bit_not<void>; // Specialization

    All special template version of functors takes any kind of argument as deduce the types from the arguments.

    Example:
    Code:
    #include <functional>
    #include <iostream>
    
    int main()
    {
        std::bit_and<int> ba; // Argument type is int
        std::bit_or<> bo; // Argument type is void
        std::bit_xor<void> bx; //Argument type is void
        std::bit_not<char> bn; //Argument type is void
    
        std::cout << ba(0x11,0x22) << std::endl; // Bitwiae AND operation of two integers. 
        std::cout << bo(0x11,0x22) << std::endl; // Bitwise OR operation of two arguments of deduced types
        std::cout << bn(~('A')) << std::endl; // Bit-wise NOT operation of the argument type char
        std::cout << bx(0x10,0x01) << std::endl; // Bit-wise XOR operation of the argument of deduced
    }
    
    Code:
    0
    51
    A
    17
    
    Predefined functor classes in STL simple classes do simple operations. They are ready made classes and can be used for STL algorithms. You can also create your own function class to do the same thing. The example below will show how predefined and user defined functors are used in STL algorithms.

    Code:
    #include <functional>
    #include <iostream>
    #include<vector>
    #include<algorithm>
    
    //Unary functor class
    struct PrintAndSumInt
    {
        int sum{ 0 };
        void operator ()(const int& i)
        {
            std::cout << i << " ";
            sum += i;
        }
    };
    
    // Binary predicate class
    struct MyIntComperator
    {
    public:
        constexpr bool operator ()(const int& n1,const int& n2) const
        {
            return n1 < n2;
        }
    };
    
    int main()
    {
        std::vector<int> v{ 3,2,7,90,34,67,89,9,7,80 };
        PrintAndSumInt print;//Unary functor
        std::cout << "Unsorted numbers:" << std::endl;
        PrintAndSumInt result= std::for_each<std::vector<int>::iterator, PrintAndSumInt>(v.begin(), v.end(), print);
        std::cout << std::endl<<"Sum of the numbers = "<<result.sum<<std::endl;
    
        std::greater<int> g; //STL unary predicate
        std::sort<std::vector<int>::iterator, std::greater<int>>(v.begin(), v.end(), g);
        std::cout<<std::endl << "Numbers in descending order: " << std::endl;
        std::for_each<std::vector<int>::iterator, PrintAndSumInt>(v.begin(), v.end(), print);
        std::cout << std::endl;
    
        MyIntComperator comp; // User defined predicate
        std::sort<std::vector<int>::iterator, MyIntComperator>(v.begin(), v.end(), comp);
        std::cout << std::endl << "Numbers in ascending order: " << std::endl;
        std::for_each<std::vector<int>::iterator, PrintAndSumInt>(v.begin(), v.end(), print);
        std::cout << std::endl;
    }
    
    Code:
    Unsorted numbers:
    3 2 7 90 34 67 89 9 7 80
    Sum of the numbers = 388
    
    Numbers in descending order:
    90 89 80 67 34 9 7 7 3 2
    
    Numbers in ascending order:
    2 3 7 7 9 34 67 80 89 90
    
    In the first sight when you see the ready made functors in STL it may not look attractive. This is because most of the functors are very light weight objects and can be created using lamda expression on the fly. But if you use STL heavily you will realize it’s benefit. If you do not want to use the STL functors and instead want to use the lamda expression or your own functors then obviously you are going to think more and type more. After all using STL function objects does not put any performance penalty but reduce development cost.
     

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