1.包装器

1.1function

std::function 是 C++ 标准库中的一个模板类,位于 <functional> 头文件中。它用于封装可调用对象,包括普通函数、Lambda 表达式、函数对象、成员函数等。std::function 提供了极大的灵活性,使得你可以将不同类型的可调用对象统一管理。

std::function 的组成部分

1.模板参数

std::function 的模板参数定义了它所封装的函数的签名。例如:

std::function<ReturnType(ParamType1, ParamType2, ...)>

ReturnType:函数的返回类型。ParamType1, ParamType2, ...:函数的参数类型。

 2.构造函数

 std::function 提供了多种构造函数,用于封装不同类型的可调用对象。

  • 默认构造函数: 
std::function<int(int, int)> func; 

创建一个空的 std::function 对象,不绑定任何可调用对象。

  •  普通函数构造
int add(int a, int b) {
    return a + b;
}

std::function<int(int, int)> func = add;

将普通函数 add 封装到 std::function 中。

  • Lambda 表达式构造: 
std::function<int(int, int)> func = [](int a, int b) { return a + b; };

 将 Lambda 表达式封装到 std::function 中。

  • 函数对象构造: 
struct Adder {
    int operator()(int a, int b) {
        return a + b;
    }
};

std::function<int(int, int)> func = Adder();

将函数对象 Adder 封装到 std::function 中。

  • 成员函数构造: 
class MyClass {
public:
    int add(int a, int b) {
        return a + b;
    }
};

MyClass obj;
std::function<int(MyClass&, int, int)> func = &MyClass::add;

 将成员函数 add 封装到 std::function 中。

#include<functional>
int f(int a, int b)
{
    return a + b;
}
struct Functor
{
public:
    int operator() (int a, int b)
    {
        return a + b;
    }
};
class Plus
{
public:
    Plus(int n = 10)
        :_n(n)
    {}
    static int plusi(int a, int b)
    {
        return a + b;
    }
    double plusd(double a, double b)
    {
        return (a + b) * _n;
    }
private:
    int _n;
};
int main()
{
    // 包装各种可调⽤对象
    function<int(int, int)> f1 = f;
    function<int(int, int)> f2 = Functor();
    function<int(int, int)> f3 = [](int a, int b) {return a + b; };

    cout << f1(1, 1) << endl;
    cout << f2(1, 1) << endl;
    cout << f3(1, 1) << endl;
    // 包装静态成员函数
    // 
    // 成员函数要指定类域并且前⾯加&才能获取地址
    function<int(int, int)> f4 = &Plus::plusi;
    cout << f4(1, 1) << endl;

    // 包装普通成员函数
    // 普通成员函数还有⼀个隐含的this指针参数,所以绑定时传对象或者对象的指针过去都可以
    function<double(Plus*, double, double)> f5 = &Plus::plusd;
    Plus pd;
    cout << f5(&pd, 1.1, 1.1) << endl;// 显式传递对象指针

    function<double(Plus, double, double)> f6 = &Plus::plusd;
    cout << f6(pd, 1.1, 1.1) << endl;// 传递对象副本
    // pd 被拷贝生成临时对象,临时对象的地址被隐式作为 this 指针。

    function<double(Plus&&, double, double)> f7 = &Plus::plusd;
    cout << f7(move(pd), 1.1, 1.1) << endl; 传递右值引用
    cout << f7(Plus(), 1.1, 1.1) << endl;//传递临时对象(右值)
    return 0;
}

参数对比

参数类型 传递方式 对象所有权 性能开销 适用场景
Plus* 指针传递 操作原对象 需修改原对象或避免拷贝
Plus 值传递(副本) 操作临时副本 拷贝构造开销 安全但低效,适用于小型对象
Plus&& 右值引用 操作临时或移动对象 无或移动开销 高效传递临时对象或资源转移

1.2bind 

调⽤bind的⼀般形式: auto newCallable = bind(callable,arg_list); 其中newCallable本⾝是⼀个可调⽤对象,arg_list是⼀个逗号分隔的参数列表,对应给定的callable的参数。当我们调⽤newCallable时,newCallable会调⽤callable,并传给它arg_list中的参数。
arg_list中的参数可能包含形如_n的名字,其中n是⼀个整数,这些参数是占位符,表示newCallable的参数,它们占据了传递给newCallable的参数的位置。数值n表⽰⽣成的可调⽤对象
中参数的位置:_1为newCallable的第⼀个参数,_2为第⼆个参数,以此类推。_1/_2/_3....这些占
位符放到placeholders的⼀个命名空间中。

 

#include<functional>
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
int Sub(int a, int b)
{
    return (a - b) * 10;
}
int SubX(int a, int b, int c)
{
    return (a - b - c) * 10;
}
class Plus
{
public:
    static int plusi(int a, int b)
    {
        return a + b;
    }
    double plusd(double a, double b)
    {
        return a + b;
    }
};
int main()
{
    auto sub1 = bind(Sub, _1, _2);
    cout << sub1(10, 5) << endl;
    // bind 本质返回的⼀个仿函数对象
    // 调整参数顺序(不常⽤)
    // _1代表第⼀个实参
    // _2代表第⼆个实参
    // ...

    auto sub2 = bind(Sub, _2, _1);
    cout << sub2(10, 5) << endl;

    auto sub3 = bind(Sub, 100, _1);
    cout << sub3(5) << endl;
    auto sub4 = bind(Sub, _1, 100);
    cout << sub4(5) << endl;

    auto sub5 = bind(SubX, 100, _1, _2);
    cout << sub5(5, 1) << endl;
    auto sub6 = bind(SubX, _1, 100, _2);
    cout << sub6(5, 1) << endl;
    auto sub7 = bind(SubX, _1, _2, 100);
    cout << sub7(5, 1) << endl;

    // 成员函数对象进⾏绑死,就不需要每次都传递了
    function<double(Plus&&, double, double)> f6 = &Plus::plusd;
    Plus pd;
    cout << f6(move(pd), 1.1, 1.1) << endl;
    cout << f6(Plus(), 1.1, 1.1) << endl;

    // bind⼀般⽤于,绑死⼀些固定参数
    function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);
    cout << f7(1.1, 1.1) << endl;
    // 成员函数指针:&Plus::plusd。
    // 对象实例:Plus()(临时对象)。
    // 参数占位符:_1 和 _2(表示调用时的参数)。

    // 计算复利的lambda
    auto func1 = [](double rate, double money, int year)->double {
        double ret = money;
        for (int i = 0; i < year; i++)
        {
            ret += ret * rate;
        }
        return ret - money;
        };
    return 0;
}
  •  头文件与命名空间
#include <functional>
using namespace std::placeholders;  // 启用占位符 _1, _2, _3

<functional>:提供 std::bindstd::function 等功能。

placeholders:定义占位符 _1(第一个参数)、_2(第二个参数)等。

  • std::bind 基础用法 
auto sub1 = bind(Sub, _1, _2);
cout << sub1(10, 5) << endl;  // 输出 (10-5)*10 = 50

作用:将 Sub 的参数按顺序绑定为 _1(第一个实参)、_2(第二个实参)。

等效调用Sub(10, 5)

  • 调整参数顺序
auto sub2 = bind(Sub, _2, _1);
cout << sub2(10, 5) << endl;  // 输出 (5-10)*10 = -50

参数交换_2 绑定到第一个参数位置,_1 绑定到第二个位置。

等效调用Sub(5, 10)

  • 固定部分参数 
auto sub3 = bind(Sub, 100, _1);
cout << sub3(5) << endl;  // 输出 (100-5)*10 = 950

auto sub4 = bind(Sub, _1, 100);
cout << sub4(5) << endl;  // 输出 (5-100)*10 = -950

 固定参数:将 Sub 的第一个参数固定为 100,第二个参数由 _1 提供。

 等效调用Sub(100, 5) 和 Sub(5, 100)

  • 绑定多参数函数 
auto sub5 = bind(SubX, 100, _1, _2);
cout << sub5(5, 1) << endl;  // 输出 (100-5-1)*10 = 940

auto sub6 = bind(SubX, _1, 100, _2);
cout << sub6(5, 1) << endl;  // 输出 (5-100-1)*10 = -960

auto sub7 = bind(SubX, _1, _2, 100);
cout << sub7(5, 1) << endl;  // 输出 (5-1-100)*10 = -960

参数绑定规则

sub5SubX(100, 5, 1)sub6SubX(5, 100, 1)sub7SubX(5, 1, 100)

  • 成员函数绑定 
function<double(Plus&&, double, double)> f6 = &Plus::plusd;
Plus pd;
cout << f6(move(pd), 1.1, 1.1) << endl;  // 输出 2.2
cout << f6(Plus(), 1.1, 1.1) << endl;     // 输出 2.2

右值引用绑定

f6 接受 Plus&&(右值引用),调用时需传递临时对象或使用 std::move

非静态成员函数 plusd 的 this 指针来自绑定的对象。

  • 使用 bind 简化成员函数调用
function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);
cout << f7(1.1, 1.1) << endl;  // 输出 2.2

固定对象实例

bind 的第一个参数是成员函数地址,第二个参数是绑定的对象实例 Plus()(临时对象)。

调用 f7 时只需传递 a 和 b,无需显式传递对象。

// 伪代码:内部行为
Plus temp = Plus();            // 创建临时对象
temp.plusd(1.1, 1.1);         // 调用成员函数

Plus::plusd:仅表示成员函数的地址,不需要对象实例。

Plus():构造临时对象供 std::bind 存储副本,用于后续调用时提供 this 指针。

2.const限定符

2.1顶层const和底层const

  • 指针本⾝是⼀个对象,它有可以指向另⼀个对象,因此指针涉及到本⾝是不是const和指向对象是不是const的问题,C++⽤为了好区分,把本⾝被const修饰叫做顶层const,把指向的对象被const修饰叫做底层const。
  • ⼤多数对象被const修饰都叫顶层const,指针被const修饰时,*左边的const叫底层const,*右边的const叫做顶层const。
  • const修饰引⽤时,这个const是底层const。

 

int main()
{
    int i = 0;
    int* const p1 = &i; // 顶层const
    const int ci = 42; // 顶层const
    const int* p2 = &ci; // 底层const
    const int& r = ci; // 底层const
    return 0;
}

2.2constexpr

constexpr 是 C++11 引入的关键字,用于声明在编译时可求值的表达式、变量或函数。它的核心目的是将计算移到编译期,提升运行时效率,并支持在需要编译期常量的上下文中使用(如数组大小、模板参数等)

constexpr 与 const 的区别

特性 constexpr const
求值时机 强制编译期求值 可能为编译期或运行时常量
适用场景 数组大小、模板参数等需编译期确定值的场景 运行时常量或只读变量
函数修饰 可修饰函数 仅修饰变量或成员函数

constexpr 变量

作用:声明变量为编译期常量。

规则

  • 必须用常量表达式初始化。

  • 所有声明为 constexpr 的变量隐含为 const

 

constexpr int size = 10;          // 正确
int arr[size];                     // 合法,数组大小需编译期确定

int runtime_val = 5;
constexpr int invalid = runtime_val; // 错误!runtime_val 不是常量表达式

constexpr 函数

作用:允许函数在编译时求值(若参数为常量表达式)。

规则

  • C++11:函数体只能包含一条 return 语句,不能有循环、局部变量等。

  • C++14 起:允许更复杂的逻辑(如循环、条件、局部变量)。

  • 参数和返回值必须是字面类型(Literal Type)。

 

// C++11 风格(简单返回)
constexpr int add(int a, int b) {
    return a + b;
}

// C++14 风格(允许复杂逻辑)
constexpr int factorial(int n) {
    int result = 1;
    for (int i = 1; i <= n; ++i) {
        result *= i;
    }
    return result;
}

constexpr int sum = add(3, 4);         // 编译时计算,sum=7
constexpr int fact_5 = factorial(5);   // 编译时计算,fact_5=120

constexpr 构造函数与对象

作用:允许在编译时构造对象。

规则

  • 构造函数必须初始化所有成员。

  • 成员函数若为 constexpr,可被编译时调用。

class Point {
public:
    constexpr Point(int x, int y) : x_(x), y_(y) {}
    constexpr int x() const { return x_; } // C++11: 必须为 const 成员函数
    constexpr int y() const { return y_; }
private:
    int x_, y_;
};

constexpr Point p(3, 4);            // 编译时构造对象
constexpr int px = p.x();           // px=3

3.处理类型

3.1auto

基本用法

auto x = 42;            // x 被推导为 int
auto y = 3.14;          // y 被推导为 double
auto str = "hello";     // str 被推导为 const char*
int a = 10;
const auto& ref = a;     // ref 是 const int&
auto ptr = &a;           // ptr 是 int*

常见应用场景

(1)简化复杂类型

std::vector<std::map<std::string, std::list<int>>> data;
auto it = data.begin();  // 无需写冗长的迭代器类型

 (2)范围for循环

std::vector<int> vec = {1, 2, 3};
for (auto num : vec) {       // 拷贝元素(num 是 int)
    // ...
}
for (const auto& num : vec) { // 避免拷贝(num 是 const int&)
    // ...
}

   (3) Lambda 表达式

auto lambda = [](int x) { return x * 2; };
std::function<int(int)> func = lambda;

(4) 返回值类型推导(C++14+)

auto add(int a, int b) {     // 返回类型自动推导为 int
    return a + b;
}

(5) 结构化绑定(C++17+)

std::pair<int, double> p = {1, 2.5};
auto [x, y] = p;        // x 是 int, y 是 double

3.2typedef和using

  • C++98中我们⼀般使⽤typedef重定义类型名,也很⽅便,但是typedef不⽀持带模板参数的类型重定义。C++11中新增了using可以替代typedef,using 的别名语法覆盖了 typedef 的全部功能,还⽀持带模板参数重定义的语法。
  • using 类型别名 = 类型;
#include <map>
#include <string>
using namespace std;

//typedef map<string, int> CountMap;
//typedef map<string, string> DictMap;
//typedef int DateType;

// using 兼容typedef的⽤法
using CountMap = map<string, int>;
using DictMap = map<string, string>;
using STDateType = int;

// using⽀持带模板参数的类型重定义
template<class Val>
using Map = map<string, Val>;

template<class Val>
using MapIter = typename map<string, Val>::iterator;

 

Logo

2万人民币佣金等你来拿,中德社区发起者X.Lab,联合德国优秀企业对接开发项目,领取项目得佣金!!!

更多推荐