Новые возможности C++11 std::function и лямбда-выражения

Linux

В C++11 добавлены такие пакеты, как std::function, std::bind и лямбда-выражения, чтобы сделать вызовы функций более удобными.

std::function

Прежде чем говорить о std::function, сначала нужно понять, что такое вызываемый объект.

Объект называется вызываемым, если выполняется одно из следующих условий:

  • это указатель на функцию
  • это объект класса (легендарный функтор) с функцией-членом operator(), лямбда-выражением
  • это объект класса, который можно преобразовать в указатель функции
  • является указателем члена класса (функции)
  • связывать выражения или другие функциональные объекты

И std::function является оболочкой для вышеупомянутого вызываемого объекта.std::function можно рассматривать как объект функции для представления абстрактного понятия функции. Экземпляр std::function может хранить, копировать и вызывать любой вызываемый объект.Сохраненный вызываемый объект называется целью std::function.Если std::function не содержит цели, он называется пустым, а пустой Вызывается std::function Цель ::function вызывает исключение std::bad_function_call.

Используйте следующий пример кода для справки:

std::function<void(int)> f; // 这里表示function的对象f的参数是int,返回值是void
#include <functional>
#include <iostream>

struct Foo {
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout << num_ + i << '\n'; }
    int num_;
};

void print_num(int i) { std::cout << i << '\n'; }

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

int main() {
    // 存储自由函数
    std::function<void(int)> f_display = print_num;
    f_display(-9);

    // 存储 lambda
    std::function<void()> f_display_42 = []() { print_num(42); };
    f_display_42();

    // 存储到 std::bind 调用的结果
    std::function<void()> f_display_31337 = std::bind(print_num, 31337);
    f_display_31337();

    // 存储到成员函数的调用
    std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
    const Foo foo(314159);
    f_add_display(foo, 1);
    f_add_display(314159, 1);

    // 存储到数据成员访问器的调用
    std::function<int(Foo const&)> f_num = &Foo::num_;
    std::cout << "num_: " << f_num(foo) << '\n';

    // 存储到成员函数及对象的调用
    using std::placeholders::_1;
    std::function<void(int)> f_add_display2 = std::bind(&Foo::print_add, foo, _1);
    f_add_display2(2);

    // 存储到成员函数和对象指针的调用
    std::function<void(int)> f_add_display3 = std::bind(&Foo::print_add, &foo, _1);
    f_add_display3(3);

    // 存储到函数对象的调用
    std::function<void(int)> f_display_obj = PrintNum();
    f_display_obj(18);
}

Из приведенного выше видно, как используется std::function.Когда std::function заполнен соответствующим списком параметров и возвращаемым значением, он становится оболочкой функции, которая может вместить все такие вызывающие методы. std::function также можно использовать как функцию обратного вызова, или если вам нужно использовать обратный вызов в C++, вы должны использовать std::function, что очень удобно Вы можете прочитать, что я писал о пулах потоков и времени. Статьи по Теме.

std::bind

С помощью std::bind можно связать вызываемый объект и параметры вместе, результат связывания сохраняется с помощью std::function, а вызов откладывается до тех пор, пока он нам не понадобится.

std::bind обычно имеет две функции:

  • Свяжите вызываемый объект с аргументами как еще одну функцию std:: для вызова
  • Преобразуйте n-арный вызываемый объект в m (m

Конкретный пример:

#include <functional>
#include <iostream>
#include <memory>

void f(int n1, int n2, int n3, const int& n4, int n5) {
    std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << std::endl;
}

int g(int n1) { return n1; }

struct Foo {
    void print_sum(int n1, int n2) { std::cout << n1 + n2 << std::endl; }
    int data = 10;
};

int main() {
    using namespace std::placeholders;  // 针对 _1, _2, _3...

    // 演示参数重排序和按引用传递
    int n = 7;
    // ( _1 与 _2 来自 std::placeholders ,并表示将来会传递给 f1 的参数)
    auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);
    n = 10;
    f1(1, 2, 1001);  // 1 为 _1 所绑定, 2 为 _2 所绑定,不使用 1001
                     // 进行到 f(2, 42, 1, n, 7) 的调用

    // 嵌套 bind 子表达式共享占位符
    auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
    f2(10, 11, 12);  // 进行到 f(12, g(12), 12, 4, 5); 的调用

    // 绑定指向成员函数指针
    Foo foo;
    auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
    f3(5);

    // 绑定指向数据成员指针
    auto f4 = std::bind(&Foo::data, _1);
    std::cout << f4(foo) << std::endl;

    // 智能指针亦能用于调用被引用对象的成员
    std::cout << f4(std::make_shared<Foo>(foo)) << std::endl;
}

лямбда-выражение

Можно сказать, что лямбда-выражение является одной из наиболее важных функций, на которые ссылается C++ 11. Оно определяет анонимную функцию, которая может захватывать определенный диапазон переменных и использовать их внутри функции.Как правило, оно имеет следующий синтаксис:

auto func = [capture] (params) opt -> ret { func_body; };

Где func — это имя, которое можно использовать как лямбда-выражение, используемое как функцию, захват — список захвата, params — список параметров, opt — параметр функции (изменяемый и т. п.), ret — тип возвращаемого значения, а func_body — это тело функции.

Полное лямбда-выражение:

auto func1 = [](int a) -> int { return a + 1; };
auto func2 = [](int a) { return a + 2; };
cout << func1(1) << " " << func2(2) << endl;

Как показано в приведенном выше коде, во многих случаях возвращаемое значение лямбда-выражения очевидно, а С++ 11 позволяет опустить определение возвращаемого значения выражения.

Лямбда-выражения позволяют захватывать переменные в определенной области:

  • [] не фиксирует никаких переменных
  • [&] Захват ссылки, захват всех переменных во внешней области видимости и использование их в качестве ссылки в теле функции
  • [=] захват значения, захват всех переменных во внешней области и использование копии в функции
  • Значение [=, &a] захватывает все переменные во внешней области видимости и захватывает переменную по ссылке
  • [a] Захватывать только переменную по значению, а не другие переменные
  • [this] захватывает указатель this в текущем классе

Пример кода для лямбда-выражения:

int a = 0;
auto f1 = [=](){ return a; }; // 值捕获a
cout << f1() << endl;

auto f2 = [=]() { return a++; }; // 修改按值捕获的外部变量,error
auto f3 = [=]() mutable { return a++; };

F2 в коде не может быть скомпилирован, потому что мы изменяем внешние переменные, захваченные значением. Фактически, лямбда-выражение эквивалентно функтору. Функтор представляет собой объект класса с функцией-членом operator(). Этот оператор() по умолчанию to Это константа, поэтому переменные-члены не могут быть изменены, а добавление mutable означает удаление атрибута const.

Вы также можете использовать лямбда-выражения для настройки правил stl, таких как пользовательское сопоставление сортировки:

struct A {
    int a;
    int b;
};

int main() {
    vector<A> vec;
    std::sort(vec.begin(), vec.end(), [](const A &left, const A &right) { return left.a < right.a; });
}

Суммировать

std::function и std::bind упрощают инкапсуляцию функций в нашем обычном процессе программирования, а лямбда-выражения доводят это удобство до крайности.Анонимные функции можно определять на месте, когда это необходимо, без необходимости определять классы или функции. и др., также очень удобны при настройке правил STL, делая код более лаконичным, гибким и повышая эффективность разработки.

использованная литература

this.cpp reference.com/me/cpp/u выносливость…

this.cpp reference.com/me/cpp/u выносливость…

this.cppreference.com/me/cpp/bad-stocks…

this.cppreference.com/me/cpp/that big…

blog.CSDN.net/no_Buddy/art…

«Углубленное применение C++11: оптимизация кода и приложение инженерного уровня»

«Эффективный современный C++»

Для получения дополнительных статей, пожалуйста, обратите внимание на мой номер принцессы V X: Программа Мяу, добро пожаловать в общение.