很多算法允许通过可调用对象自定义计算逻辑的细节
transform
如何定义可调用对象:
函数指针:概念直观,但定义位置受限
c++不支持在函数内部定义函数,所以位置是受限的。
类:功能强大,但书写麻烦
通过操作符重载,为类定义可调用对象。
bind :基于已有的逻辑灵活适配,但描述复杂逻辑时语法可能会比较复杂难懂
lambda 表达式:小巧灵活,功能强大
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <sstream>
#include <iterator>
#include <functional>
using namespace std;
bool MyPredict2(int val1, int val2)
{
return val1 > val2;
}
int main()
{
using namespace std::placeholders;
std::vector<int> x{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector<int> y{};
std::copy_if(x.begin(), x.end(), std::back_insert_iterator(y), std::bind(MyPredict2, _1, 3));
for (auto p : y)
{
cout << p << ' '; // 4-10
cout << endl;
}
}
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <sstream>
#include <iterator>
#include <functional>
using namespace std;
bool MyPredict2(int val1, int val2)
{
return val1 > val2;
}
int main()
{
using namespace std::placeholders;
auto x = std::bind(MyPredict2, _1, 3); // _1代表调用x时,接收x的第1个参数
x(50); // 50会作为MyPredict2的第1个参数
cout << x(50) << endl; // 1 true
}
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <sstream>
#include <iterator>
#include <functional>
using namespace std;
bool MyPredict2(int val1, int val2)
{
return val1 > val2;
}
bool MyAnd(bool val1, bool val2)
{
return val1 && val2;
}
int main()
{
using namespace std::placeholders;
auto x1 = std::bind(MyPredict2, _1, 3);
auto x2 = std::bind(MyPredict2, 10, _1);
auto x3 = std::bind(MyAnd, x1, x2);
cout << x3(5) << endl; // 1 意思为:3 < 5 < 10
}
调用 std::bind 时,传入的参数会被复制,这可能会产生一些调用风险
用智能指针解决
可以使用 std::ref 或 std::cref 避免复制的行为
bind还有一个缺点,就是复杂逻辑很难看懂代码。
std::bind_front ( C++20 引入): std::bind 的简化形式
绑定第一个参数。
参数与函数体:
int main()
{
auto x = [](int val) { return val > 3; }; // (函数形参) {函数体,多条语句,要用分号隔离}
cout << x((5)) << endl; // 1, 5 > 3
}
int main()
{
auto x = [](int val) { return (val > 3) && (val < 10); };
cout << x((5)) << endl; // 1
}
返回类型的自动推导c++11。如果引入更多的return语句,所有的return语句的类型必须是相同的,这是前提。
int main()
{
auto x = [](int val)
{
if (val > 3)
{
return 3.0;
}
else
{
return 1.5;
}
};
cout << x((5)) << endl; // 3,自动推导x的类型是double
}
int main()
{
// 告诉编译器返回类型
auto x = [](int val) -> float
{
if (val > 3)
{
return 3.0;
}
else
{
return 1.5f;
}
};
cout << x((5)) << endl; // 3
}
捕获: 针对函数体中使用的局部自动对象进行捕获
int main()
{
int y = 10;
auto x = [](int val)
{
return val > y; // 报错,在lambada表达式内部,不知道y是什么
};
cout << x((5)) << endl;
}
int main()
{
int y = 10; // 局部自动对象
auto x = [y](int val)
{
return val > y; // 把y放到[]内进行捕获
};
cout << x((5)) << endl;
}
int main()
{
static int y = 10; // y是一个局部静态对象,不需要捕获,直接使用
auto x = [](int val)
{
return val > y;
};
cout << x((5)) << endl;
}
值捕获
int main()
{
int y = 10;
auto x = [y] (int val) mutable
{
++y;
return val > y;
};
cout << x((5)) << endl;
cout << y << endl; // 10 lambada对y的修改,不会传递到外部,因为是值捕获,y是复制到lambada内部
}
引用捕获
int main()
{
int y = 10;
auto x = [&y] (int val)
{
++y;
return val > y;
};
cout << x((5)) << endl;
cout << y << endl; // 11 y是引用捕获
}
混合捕获
int main()
{
int y = 10;
int z = 3;
// 对y进行引用捕获,对z进行值捕获 --> 混合捕获
auto x = [&y, z] (int val) mutable
{
++y;
return val > z;
};
cout << x((5)) << endl; // 1
cout << y << endl; // 11
}
如果lambada[捕获列表]里要捕获的值太多,这样就比价麻烦。大部分对象如果都是值捕获的,我们在[]里写个=。所有在{}使用的局部自动对象都值捕获了。我们也可以在[]中写入&,表示在{}使用的局部自动对象都引用捕获了。
int main()
{
int y = 10;
int z = 3;
// []除了z是值捕获,其他都是引用捕获
auto x = [&, z] (int val) mutable
{
++y;
return val > z;
};
cout << x((5)) << endl;
cout << y << endl;
}
this 捕获
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <sstream>
#include <iterator>
#include <functional>
using namespace std;
struct Str{
auto fun()
{
int val = 3; // 局部自动对象
// this 表示如果构造了Str对象,this就是一个指针,指向Str对象的地址
auto lam = [val, this] ()
{
return val > x;
};
return lam();
}
int x;
};
int main()
{
Str s;
s.fun();
}
初始化捕获( C++14 )
int main()
{
int x = 3;
// 初始化捕获,构造对象y,将x赋予y
auto lam = [y = x](int val)
{
return val > y;
};
cout << lam(100) << endl;
}
int main()
{
int x = 3;
int y = 10;
// z是在编译期计算出来的
auto lam = [z = x + y](int val)
{
return val > z;
};
cout << lam(100) << endl;
}
*this 捕获( C++17 )
说明符
mutable
int main()
{
int y = 3;
auto lam = [y](int val) mutable
{
++y; // 不能通过编译,除非加上mutable
return val > y;
};
}
constexpr (C++17)
int main()
{
int y = 3;
auto lam = [y](int val) constexpr
{
return val + 1; // 表示这个lam可以在编译期进行调用
};
constexpr int val = lam(100);
cout << val << endl;
}
consteval (C++20)
int main()
{
int y = 3;
auto lam = [y](int val) consteval
{
return val + 1; // consteval只能在编译期调用
};
constexpr int val = lam(100);
cout << val << endl;
}
捕获时计算
int main()
{
int x = 3;
int y = 5;
// 捕获时计算
auto lam = [z = x + y]()
{
return z;
};
lam();
}
即调用函数表达式( Immediately-Invoked Function Expression, IIFE )
int main()
{
int x = 3;
int y = 5;
// 捕获时计算
const auto val = [z = x + y]()
{
return z;
}(); // 即调用函数表达式( Immediately-Invoked Function Expression, IIFE )
// 构造完lam,构造完,马上执行lam 表达式
// 执行完的值,马上赋给val,不需要额外定义函数
}
使用 auto 避免复制( C++14 )
int main()
{
auto lam = [](auto x)
{
return x + 1;
};
}
int main()
{
std::map<int, int> m{{2, 3}};
// 虽然用了引用,但是还是复制了
auto lam = [](const std::pair<int, int>& p)
{
return p.first + p.second;
};
cout << lam(*m.begin()) << endl; // 5
}
int main()
{
std::map<int, int> m{{2, 3}};
auto lam = [](const auto& p)
{
return p.first + p.second;
};
cout << lam(*m.begin()) << endl; // 5
}
Lifting ( C++14 )
#include <iostream>
#include <map>
using namespace std;
auto fun(int val)
{
return val + 1;
}
auto fun(double val)
{
return val + 1;
}
int main()
{
// auto x,来决定用哪个fun
auto lam = [](auto x)
{
return fun(x);
};
cout << lam(3) << endl; // 4
cout << lam(3.5) << endl; // 4.5
// auto b = std::bind(fun, 3); // bind不知道调用哪个fun了
// cout << b() << endl;
}
递归调用( C++14 )
#include <iostream>
#include <map>
using namespace std;
/*
int factorial(int n)
{
return n > 1 ? n * factorial(n - 1) : 1;
}*/
int main()
{
// 这么写递归调用的lambada表达式会报错
auto factorial = [](int n){
return n > 1 ? n * factorial(n - 1) : 1;
};
cout << factorial(5) << endl; // 120
}
#include <iostream>
#include <algorithm>
#include <vector>
#include <ranges>
using namespace std;
int main()
{
std::vector<int> x{1, 2, 3, 4, 5};
// auto it = std::ranges::find(x.begin(), x.end(), 3);
auto it = std::ranges::find(x, 3);
cout << *it << endl;
}
view并不是对输入的东西立即计算,而是需要的时候计算,把提升程序的性能。view模糊了容器和算法的概念了。