C++ Lambda

Chapter I Lambda in C++98/03

Estimated reading time of 4 minutes 57 Views

1. Lambdas in C++98/03

Last Modified: 28 August 2024

本章的主要内容

  1. How to pass the functor to algorithms in the standard library
  2. Limit of functions and function pointer (funaction pointer)
  3. Why isn't the function aids good enough?

在 C++98/03 中的可调用对象(callable object)

In the design concept of the C++ standard library, algorithms such as "std::sort", "std::for_each", "std:transform" can accept any callable object (callable objects) and apply it to input into the packaging, however, in the C++98/03 standard, these algorithms can only accept functional pointers and imitations (functional objects).

Use a function here to output the element in vector.
General realization:

#include <algorithm>
#include <iostream>
#include <vector>

void PrintFunc(int x){
    std::cout << x << '\n';
}

int main(){
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    for_each(v.begin(), v.end(), PrintFunc);
}
/* output:
 * 1
 * 2
 */
C++

The code above is std: :for_each runs through the vector container (for reasons C++98/03 is not supported), and it transfers PrintFunc as a callable object.

Convert this function to a analogue function (functor):

#include <iostream>
#include <vector>
#include <algorithm>

struct PrintFunctor{
    void operator()(int x) const {
        std::cout << x << '\n';
    }
};

/* or this version
class PrintFunctor{
public:
    void operator()(int x) const {
        std::cout << x << '\n';
    }
};
*/

int main(){
    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    for_each(v.begin(), v.end(), PrintFunctor());
}
/* output:
 * 1
 * 2
 */
C++

The above example uses an imitation function with an optator(s).

Function pointers, however, are usually non-state, i.e. they point to only one function and do not store any additional information or state, as compared to imitations, which allow them to store between multiple calls.

Defines a simple imitation function that records the number of times it is called. The key is to define the state in the imitation function:

#include <algorithm>
#include <iostream>
#include <vector>

class printFunctor{
public:
    printFunctor(const std::string str): strText(str), numCalls(0) { }
    void operator()(int x){
        std::cout << strText << x << '\n';
    }
    int getNumCalls() const{
        return numCalls;
    }
private:
    std::string strText;
    mutable int numCalls;
};

int main(){
    std::vector v = {1, 2, 3, 4, 5, 6};
    std::string preText = "Elem: ";
    printFunctor visitor =
    std::for_each(v.begin(), v.end(), printFunctor(preText));
    std::cout << "numCalls: " << visitor.getNumCalls() << '\n';
}
/* output:
 * 1
 * 2
 * 3
 * 4
 * 5
 * 6
 * numCalls: 6
 */
C++

仿函数(functor)的问题

While a separate class can be used to design a function, it is difficult to read and maintain a function at a different location from the algorithm, which results in the function code being located far away from the algorithm to be used in the source file.

In C++98/03, one limitation is that local types (types defined within functions) cannot be used as template parameters, such as:

int main(){
    /* define a type inside a function */
    struct PrintFunctor{
        void operator()(int x) const{
            std::cout << x << '\n';
        }
    };

    std::vector<int> v(10, 1);
    std::for_each(v.begin(), v.end(), PrintFunctor());
}
C++

The following errors can be made using the -std=C++98 standard of the GCC:

error: template argument for
'template<class _IIter, class _Funct> _Funct
std::for_each(_IIter, _IIter, _Funct)'
uses local type 'main()::PrintFunctor'
C++

使用 辅助函数(functional helper) 解决

In Standard Library <functional> Headers, many types and functions can be used with standard algorithms:
std::plus<T>(): Accept two parameters and return them together.
std::minus<T>(): Accept two parameters and return their differences.

#include <functional>
#include <iostream>
#include <vector>
#include <algorithm>

int main(){
    std::vector<int> vi1 = {1, 2, 3, 4, 5, 6};
    std::vector<int> vi2 = {2, 3, 4, 5, 6, 7};
    std::vector<int> vRes(vi1.size());

    std::transform(vi1.begin(), vi1.end(), vi2.begin(),
                    vRes.begin(), std::plus<int>());
    std::cout << "Res for plus: ";
    for(int n: vRes) std::cout << n << ' ';

    std::transform(vi1.begin(), vi1.end(), vi2.begin(),
                    vRes.begin(), std::minus<int>());
    std::cout << "\nRes for minus: ";
    for(int n: vRes) std::cout << n << ' ';
}
/* output:
 * Res for plus: 3 5 7 9 11 13
 * Res for minus: -1 -1 -1 -1 -1 -1
 */
C++

std::less<T>(): Accepts two parameters and returns if the first parameter is smaller than the second.
std::greater_equal<T>(): Accepts two parameters and returns whether the first parameter is greater than or equal to the second parameter.
std::bind1st: Creates a callable object that fixes the first parameter to a given value.
std::bind2nd: Creates a callable object and fixes the second parameter to a given value.

Benefits of using auxiliary functions:

#include <algorithm>
#include <functional>
#include <vector>

int main(){
    std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9};

    const size_t smaller5 = std::count_if(
    v.begin(), v.end(),
    std::bind2nd(std::less<int>(), 5));

    return smaller5;
}
C++

In the code above, use std::bind2nd to bind 5 to the second parameter (x < 5) of the std::less function (x < y), which returns the value return x < 5.

const size_t greater5 = std::count_if(
v.begin(), v.end(),
std::bind1st(std::less<int>(), 5));
C++

Similarly, using std::bind1st bound 5 to the first parameter (5 y), returns the value return 5 < x.

Supplement: use of std:bind

#include <iostream>
#include <functional>

int add(int x, int y){
  std::cout << "1st param: " << x << '\n';
  return x + y;
}

int main(){
  /* 创建一个绑定第一个参数为10的函数对象 */
  auto res = std::bind(add, 10, std::placeholders::_1);

  std::cout << "12 + 10 = " << res(12) << '\n';
}
/* output:
 * 12 + 10 = 1st param: 10
 * 22
 */
C++

In many cases, however, a function is achieved with a number of functions, and the syntax here is complicated:

std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};
const size_t val = std::count_if(v.begin(), v.end(),
                           std::bind(std::logical_and<bool>(),
                           std::bind(std::greater<int>(), _1, 2),
                           std::bind(std::less_equal<int>(), _1, 6)));
C++

The result of the above complex syntax is a return x > 2 & x < = 6.
These issues have improved in the C++ 11 that follows.

Leave a Comment

Share this Doc

Chapter I Lambda in C++98/03

Or copy link

CONTENTS
Remember to rest.