编译期的整数操作
模板元编程是一个挺有意思 (但是毫无卵用) 的东西。比如我们可以实现编译期的快速排序。(但是 constexpr
函数基本把这部分给废掉了)
导入
下面是一个例子:
template <int A, int B>
struct add {
static constexpr int value = A + B; };
cout << add<1, 2>::value;
输出 3,但是这个 3 是在编译期计算出来的而不是运行时。不过实际上。。
template <typename T>
constexpr T add(T a, T b) {
return a + b; }
就可以实现一样的东西,而且看起来更直观。
那么我们学这个有什么用呢?(纯粹好玩)
基本类型
integer_constant
我们希望一些运算的工作放在编译期完成,那么我们就需要一些东西来存储或表示我们的运算结果。我们从导入中提到的例子能看到,我们表示一个编译期的整数,都是在模板的参数中表示。那么我们就知道了应该如何表示一个编译期的常量:
template <typename T, T v>
struct integral_constant {
static constexpr T value = v;
typedef T value_type;
constexpr operator T() const noexcept {
return value; }
constexpr T operator()() const noexcept {
return value; }
};
template <int v>
using integer_constant = integral_constant<int, v>;
这样我们就能在模板参数中表示一个整型的常量,常量的值就在模板参数中,比如:integer_constant<5>
这个类型就能表示 5。
integer_sequence
函数式编程中另一个重要的数据结构就是列表,我们再来看如何表示一个列表:
template <typename T, T... values>
struct integral_sequence {
};
template <int ... values>
using integer_sequence = integral_sequence<int, values...>;
这里利用了可变模板参数来实现列表。这样我们就可以通过 integer_sequence<1, 2, 3, 4, 5>
来表示列表:[1, 2, 3, 4, 5]
。
基本函数
现在有了基本数据类型,我们就可以在这上面构建一系列的函数了。我们已经知道了模式匹配,那么我们就可以照猫画虎地写出相应的基本函数:
length
template <typename integer_sequence>
struct length;
template <int... values>
struct length<integer_sequence<values...>> : integral_constant<size_t, sizeof...(values)> {
};
template <typename T>
constexpr size_t length_v = length<T>::value;
push_front
template <int V, typename integer_sequence>
struct push_front;
template <int V, int... values>
struct push_front<V, integer_sequence<values...>> {
using type = integer_sequence<V, values...>;
};
template <int V, typename T>
using push_front_t = typename push_front<V, T>::type;
push_front_t<1, integer_sequence<2, 3, 4, 5>> === integer_sequence<1, 2, 3, 4, 5>;
empty
template <typename integer_sequence>
struct empty;
template <>
struct empty<integer_sequence<>> : std::true_type {
};
template <int... values>
struct empty<integer_sequence<values...>> : std::false_type {
};
template <typename T>
constexpr bool empty_v = empty<T>::type::value;
empty_v<integer_sequence<>> == true;
empty_v<integer_sequence<1, 2, 3