模板元编程与函数式编程
模板元编程
1 2 3 4 5 6 7 8 9 10 11
| template <class T=int> T two(){ return 2; }
template <int N> void show_time(std::string s){ for(int i=0;i<N;++i){ std::cout<<s<<std::endl; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include<iostream> #include <time.h>
int sum(int n,bool debug){ int res=0; for(int i=0;i<n;++i){ res += i; if(debug) std::cout<<i<<"-th: "<<res<<std::endl; } return res; }
template <bool debug> int sum(int n){ int res=0; for(int i=0;i<n;++i){ res += i; if(debug) std::cout<<i<<"-th: "<<res<<std::endl; } return res; }
int main(){ int n; std::cin >> n; auto st=clock(); std::cout<<sum(n ,true)<<std::endl; std::cout<<sum(n,false)<<std::endl; auto ed=clock(); std::cout<<ed-st<<"ms"<<std::endl; st=ed; std::cout<<sum<true>(n)<<std::endl; std::cout<<sum<false>(n)<<std::endl; ed=clock(); std::cout<<ed-st<<"ms"<<std::endl; return 0; }
|
第一种sum每次循环的时候都需要做判断,对于性能有影响,而且编译器不好优化。后者(第二种sum)在编译器看来就是 if(false)
,显然会被自动优化掉。
更进一步,可以用C++17的if constexpr
保证编译器确定的分支。
假如有一个函数(有可能是错误的),暂时未被调用,我们可以采用模板的惰性,只要没有被调用就不会实例化,因此可以加快编译速度。
1 2 3 4
| auto &product_table(){ static std::map<string,int> instance; return instance; }
|
- 可以通过
decltype
获取表达式的类型,typeid
只有在有虚函数的时候才会起作用。
对于我想定义一个和之前定义过的函数一样的变量/函数,可以使用
declgtype(auto) p=func();
decltype(func()) p=func();
using
typedef std::vector<int> VecInt;
和using VecInt=std::vector<int>;
等价
typedef int (*PFunc)(int);
和using PFunc=int(*)(int)
等价
1 2 3 4 5 6 7 8 9
| template<class T1,class T2> auto add(std::vector<T1> const& a, std::vector<T2> const& b){ using T0=decltype(T1{}+T2{}); std::vector<T0> vec; for(int _=0;_<a.size();++_){ vec.push_back(vec[_]); } return vec; }
|
函数式编程
函数也可以作为另一个函数的参数
- 在lambda表达式中
[&]
用于捕获同一作用域中的变量。(闭包)此外还可以修改同一作用域下的变量
- 但是可能会出现如下问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include<iostream> #define debug(x) std::cout<<#x<<": "<<x<<std::endl
template<class Func> void call_twice(Func const& func){ debug(func(0)); debug(func(1)); debug(sizeof(Func)); }
auto make_twice(int fac){ return [&](int n){ return n*fac; }; }
int main(){ auto twice = make_twice(2); call_twice(twice); return 0; }
|
输出为
1 2 3
| func(0): 0 func(1): 6422040 sizeof(Func): 8
|
这是因为make_twice
函数在调用之后会对fac进行销毁,但是之前传入的是函数的引用,所在在fac销毁之后其指针会随意指向某处,导致输出很怪。
小彭老师的建议是对make_twice()函数进行修改:
1 2 3 4 5
| auto make_twice(int fac){ return [=](int n){ return n*fac; }; }
|
这样输出就没有问题了
1 2 3
| func(0): 0 func(1): 2 sizeof(Func): 4
|
此时可以把fac作为一个值存下来,而不是存下fac的指针。
因此,[&]需要保证lambda对象的声明周期不超过其捕获的所有引用的声明周期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include<iostream> #include<functional> #define debug(x) std::cout<<#x<<": "<<x<<std::endl
void call_twice(std::function<int(int)> const& func){ debug(func(0)); debug(func(1)); debug(sizeof(func)); }
std::function<int(int)> make_twice(int fac){ return [=](int n){ return n*fac; }; }
int main(){ auto twice = make_twice(2); call_twice(twice); return 0; }
|
这种形势下function<int(int)>
是虚函数,会有额外的开销
1 2 3
| func(0): 0 func(1): 2 sizeof(func): 32
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream> #include <vector> #include <type_traits>
template<class Func> void fetch_data(Func const& func){ for(int i=0;i<32;++i){ func(i); func(i+0.5f); } }
int main(){ std::vector<int> res_i; std::vector<float> res_f; fetch_data([&] (auto const &x){ using T = std::decay_t<decltype(x)>; if constexpr (std::is_same_v<T, int>){ res_i.push_back(x); }else if constexpr (std::is_same_v<T, float>){ res_f.push_back(x); } } ); return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> #include <vector> #include <set>
int main(){ std::vector<int> arr={1,4,2,8,5,7,1,4}; std::set<int> visited; auto dfs = [&] (auto const &dfs, int index) ->void{ if(visited.find(index)==visited.end()){ visited.insert(index); std::cout<<index<<std::endl; int next=arr[index]; dfs(dfs, next); } }; dfs(dfs, 0); return 0; }
|
std::tuple<...>
可以讲多个不同类型的值打包成一个。尖括号里填各种类型
之后可以使用std::get<0>([tuplename])
获取对应索引值
- 结构化绑定
1 2
| auto tup = std::tuple(3,3.14,"6"); auto [x,y,z] = tup;
|
tuple
还可以使用在有多个返回值的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> #include <cmath> #include <optional> #include <memory> #include <vector>
std::optional<float> mysqrt(float x){ if(x>=0.f) { return std::sqrt(x); }else{ return std::nullopt; } }
int main(){ auto res = mysqrt(-3.f); if(res.has_value()){ std::cout<<"answer: "<<res.value()<<std::endl; }else{ std::cout<<"failed"<<std::endl; } return 0; }
|
optional
:operator bool()
和has_value()
等价,是一个更安全的指针
variant
是更安全的union
,存储多个不同类型的值
我的作业:https://github.com/WangYuHang-cmd/hw03