简单记录一下近期学习的一些魔法,
有些魔法会需要c++20才能用,编译的时候建议直接开c++20,
std::move, std::forward
右值引用、完美转发啥的,神奇玩意,还没完全搞懂,
constexpr
const
的进阶版,可以用来修饰变量,函数,lambda表达式,
告诉编译器这是一个可以在编译器计算的东西,这样在编译器即可计算,无需等到运行期,
配合static_assert
可以做到编译时自定义报错。示例代码见std::index_sequence
的示例代码;
这个还能用在if
后面做编译期的条件判断,
if constexpr () {};
Attention
要注意编译期的计算是很慢的(慢10倍左右),并且有计算总次数限制,可以使用编译开关-fconstexpr-ops-limit=
来自定义,
在多文件编译的场景下,每个.cpp
的constexpr是相互独立的,也就是每个cpp有着单独的constexpr-ops-limit
,并且同一个.h中的同一个constexpr在不同的.cpp会被重复计算多次,哪怕最终运行期的地址相同。
std::index_sequence
在编译期的整数序列,和std::make_index_sequence
配合使用,在编译期生成整数数列。
最简单的用法:
template <size_t... idx>
auto IdxSeq(std::index_sequence<idx...>)
{
return std::array{idx...};
}
auto idxSeq = IdxSeq(std::make_index_sequence<5>{});
// idxSeq = {0, 1, 2, 3, 4}
比较好的完整讲解:C++雾中风景16:std::make_index_sequence, 来试一试新的黑魔法吧
接下来是一个大杂烩代码:
Code
#include <array>
#include <iostream>
#include <cassert>
constexpr int MAXN = 3;
template <size_t... N>
static constexpr auto GeneI(std::index_sequence<N...>)
{
constexpr int n = sizeof...(N);
// tamplate for labmda was allowed after c++20
auto GeneJ = []<size_t... M>(int line, std::index_sequence<M...>) constexpr
{
return std::array{int((n * line + M))...};
};
return std::array{GeneJ(N, std::make_index_sequence<n>{})...};
}
constexpr auto a = GeneI(std::make_index_sequence<MAXN>{});
// equivalent :
constexpr std::array<std::array<int, MAXN>, MAXN>
a1 = {{{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}};
int main(int argc, char *argv[])
{
static_assert(a[0][0]); // compile error: static assertion failed
static_assert(a[0][1]); // OK
}
template <typename… Args>
可变模版参数的展开,在编译期完成,
典型用法是做参数转发:
template <typename... Args>
auto func1(Args &&...args)
{
return func2(std::forward<Args>(args)...);
}
还可以用来做可变入参数量的函数:
template <typename... Args>
auto sum(Args &&...args) { return (... + args); }
sum(1, .5, true); // 2.5
sum(std::string("aa"), "bb"); // "aabb"
折叠表达式
上面示例代码用的... +
就是折叠表达式的一种,
理解不难,见官方文档:
cppreference.com - fold expression
改Class中的Private成员
我看不懂,但我大受震撼
#include <iostream>
#include <stdint.h>
using namespace std;
class Bank {
public:
void Check()
{
cout << "bank: " << money << endl;
}
private:
size_t money = 1100;
};
template <auto M>
struct Tunnel;
template <typename T, typename U, T U::*M>
struct Tunnel<M> {
friend T& Sneak(U& u)
{
return u.*M;
}
};
template struct Tunnel<&Bank::money>;
size_t& Sneak(Bank&);
int main()
{
Bank bank;
bank.Check();
auto& takeCtl = Sneak(bank);
cout << "theif: " << takeCtl << endl;
takeCtl = 0;
bank.Check();
}