Callable 可调用对象 lambda表达式 “匿名函数“

本文详细介绍了C++中Callable对象的概念,包括函数、函数指针、函数对象(functor)、lambda表达式以及std::bind和std::function的作用。重点展示了lambda表达式的灵活性,以及这些工具如何提高代码的通用性和灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1)什么是可调用对象? 

        Callable 可调用对象 支持函数调用运算符() 

        在C++中, Callable可调用对象有如下: 
                (1) 函数    
                (2) 函数指针 
                (3) 函数对象 functor "仿函数"   
                        只要该对象所属的类,实现了 函数调用运算符重载 operator() 
                (4) lambda表达式

例子: 

template<typename T>
void Swap( T& a, T& b )
{
    T tmp = a;
    a = b;
    b = tmp;
}

//冒泡排序 
template<typename T, typename Caller>
void MPsort( T *begin, T *end, Caller less )  
//less类似于一个函数 bool (T& a, T& b)
{
    T *p = end-1;

    for( ; begin < end ; begin++ )
    {
        for( p=end-1; p>begin; p-- )
        {
            //if( *p < *(p-1) )
            if( less(*p, *(p-1)) )
            {
                //交换位置
                Swap( *p, *(p-1) );
            }
        }
    }
}

bool pk( const int& a, const int& b)
{
    return a<b;
}

class Less
{
public:
    // () 函数调用运算符重载
    bool operator()( const int& a, const int& b )
    {
        return a<b;
    }
};

int main()
{
    int a[10] = {1,3,5,7,9,2,4,6,8,0};
    MPsort( a, a+10 );	// [ , )

    //1.函数 
    //pk是一个函数,pk后面可以接(), 因此pk就是一个Callable
        MPsort( a, a+10, pk );

    //2.函数指针
        bool (*p)(const int& , const int&);
        p = pk;
        MPsort( a, a+10, p );	//函数指针p 也是一个Callable

    //3.函数对象 functor "仿函数"
        Less less;	//函数对象, 也是一个Callable
        MPsort( a, a+10, less );
    
    //4.lambda表达式,也是一个Callable
        //...

    for( auto &s : a )
    {
        cout << s << " ";
    }
    cout << endl;
}

lambda表达式 被用来表示一种匿名的函数
所谓的匿名函数,简单的理解就是 没有名字的函数, 也常称为: lambda表达式 / lambda函数 

lambda匿名函数 的定义语法: 
[外部变量的访问方式的说明符] (形式参数) mutable noexcept/throw() -> 返回值类型 
{
//...  函数体/语句 
}

(1) [外部变量的访问方式的说明符]
        []不能省略
        []中括号 用于向编译器表明是一个lambda表达式
        []中括号内部 可以注明当前lambda函数的函数体中,可以使用哪些"外部变量"
            "外部变量",指的是 和 lambda表达式 位于同一个作用域内的所有的局部变量
        [] 用于向编译器表明,该匿名函数需要"捕获"哪些外部变量 
            lambda引入"外部变量"的方式 
                []  空, 表示当前的lambda匿名函数不导入任何的外部变量
                [=] 只有一个=等于号,表示是以值传递的方式导入所有的外部变量
                [&] 只有&引用符号,表示以 引用传递的方式导入所有的外部变量 
                [val1, val2, ...] 表示以值传递的方式导入val1,val2等指定的多个外部变量,顺序无关 
                值传递的方式捕获外部变量,会在定义lambda表达式时,生成它的一个拷贝


                [&val1, &val2, &...] 表示以引用传递的方式导入val1,val2等指定的多个外部变量,
                [ val1, &val2, ... ] 表示以值传递的方式导入val1, 以引用传递的方式导入val2 
                [=, &val1]  除了val1是引用传递的方式导入以外,其他的都是值传递的方式导入
                [this] 表示以值传递的方式导入当前的this指针 

                    // [=, val1]   //error 单个变量不允许以相同的传递方式导入多次 

(2) (形式参数) 
    和普通函数的定义一样, lambda函数也可以接收外部传递过来的多个参数
        和普通函数不同的是: 
            如果不需要传递参数, 连同()小括号一起省略

(3) mutable 
    "可变的", 此关键字可以省略, 如果使用了 则之前的()就不能省略了 
    默认情况下: 
        对于以值传递的方式导入的外部变量,不运行来lambda函数体内修改它们的值 
                (可以理解为 这部分变量都是 const常量 )
        如果想修改它们,则必须使用 mutable这个关键字
    注意: 
        对于以值传递的方式引入的外部变量,lambda表达式修改的只是拷贝的那一份 
        并不会修改真正的外部变量 

(4) noexcept/throw() 
    可省略的, 如果使用了 则之前的()就不能省略了

    noexcept 表示匿名函数体内不会抛出异常, 默认情况下(不加noexcept) 匿名函数体内可以抛出异常的
    throw() 用来指定函数体内抛出的异常的类型

(5) -> 返回值类型 
    指定匿名函数的返回值类型 
        如果lambda函数体内只有一个return语句,或者该函数返回值为void 
        则编译器可以自行推断返回值类型,在这种情况下 可以直接省略 

(6) 函数体 
    和普通函数一样, lambda匿名函数包含的代码都必须放在函数体内 
    该函数体内 除了可以使用自己定义的内部变量(包括参数), 还可以直接使用引入的外部变量
    当然也可以使用全局变量 

lambda表达式可以 形成一个 "闭包" ,可以捕获外部变量或对象 

例子: 
    // 4.lambda表达式 , 也是一个Callable可调用对象

MPsort( a, a+10, [](const int& a, const int& b) -> bool { return a < b; }  );


MPsort( a, a+10, [](const int& a, const int& b) { return a < b; }  );

C++11开始引入的可调用对象的其他形式,如std::bind的结果和std::function的实例。

5. std::bind
std::bind用于绑定一个可调用对象的参数,并返回一个新的可调用对象。

#include <iostream>  
#include <functional>  
  
// 普通函数  
void print_sum(int a, int b) {  
    std::cout << a + b << std::endl;  
}  
  
int main() {  
    // 使用std::bind绑定print_sum的第一个参数为10  
    auto bound_print_sum = std::bind(print_sum, 10, std::placeholders::_1);  
      
    // 调用bound_print_sum,只需提供一个参数  
    bound_print_sum(5); // 输出15  
      
    return 0;  
}


6. std::function
std::function是一个通用的、多态的函数封装器。它可以存储、复制和调用任何可调用对象,如函数、lambda表达式、函数对象或其他std::function对象。
 

#include <iostream>  
#include <functional>  
#include <vector>  
#include <algorithm>  
  
int main() {  
    std::vector<int> numbers = {1, 2, 3, 4, 5};  
      
    // 使用std::function来存储任何可调用对象  
    std::function<int(int)> operation;  
      
    // 赋值一个lambda表达式给operation  
    operation = [](intn) { return n * n; };

// 使用std::transform和std::function来将每个数平方
std::transform(numbers.begin(), numbers.end(), numbers.begin(), operation);

// 输出结果
for (int n : numbers) {
    std::cout << n << ' ';
}
std::cout << std::endl;

// 更改operation以指向另一个可调用对象
operation = [](int n) { return n * 3; };

// 再次使用std::transform来将每个数乘以3
std::transform(numbers.begin(), numbers.end(), numbers.begin(), operation);

// 输出结果
for (int n : numbers) {
    std::cout << n << ' ';
}
std::cout << std::endl;

return 0;
}

        在这个例子中,`std::function<int(int)>`表示一个接受一个`int`参数并返回`int`的可调用对象。我们首先将一个lambda表达式赋值给`operation`,然后使用它来计算每个数的平方。接着,我们更改`operation`以指向另一个lambda表达式,这次我们将每个数乘以3。 每种可调用对象都有其适用的场景。函数和函数指针简单直接,适用于简单的函数调用。Lambda表达式提供了匿名函数和捕获局部变量的能力,非常灵活。函数对象(仿函数)允许你封装更复杂的逻辑,并可以像使用对象一样使用它们。

        `std::bind`和`std::function`提供了更高级的功能,如参数绑定和可调用对象的类型擦除,使得代码更加通用和灵活。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值