基于 Walter E. Brown 的演讲 - Modern Template Metaprogramming (1)
示例代码可以贴到 www.onlinegdb.com 直接运行,需要选择C++17
总结的内容
- 元编程中最主要的两个变量就是 ::value 和 ::type
- 递归是通过偏特化定义终止条件来实现的
返回值的元函数
C++ 模板元编程是在编译时执行的代码,所以参数和返回值都必须是编译时确定。和普通的 C++ 代码不同,元编程的参数是通过 <> 传入的,返回值就是访问类中的静态成员常量。
下面是一个求绝对值的函数,其中 static_assert 是在编译时求值的。
#include <climits> // INT_MIN
template< int N >
struct abs {
static_assert(N != INT_MIN);
static constexpr int value = (N < 0) ? -N : N;
};
int main() {
static_assert(abs< 42 >::value == 42);
static_assert(abs< -42 >::value == 42);
}
下面的写法也是在编译时执行的,但是上面 struct 的写法更灵活。
constexpr int abs(int N) { return (N < 0) ? -N : N; }
int main() {
static_assert(abs(-42) == 42);
}
元函数的递归
template < unsigned M, unsigned N >
struct gcd {
static constexpr int value = gcd< N, M%N >::value;
};
template< unsigned M >
struct gcd< M, 0 > {
static_assert(M != 0);
static constexpr int value = M;
};
int main() {
static_assert(gcd< 15,5 >::value == 5);
}
入参是类型的元函数
和普通的函数不同,元函数的参数可以是类型。
下面的元函数返回给定数组的维度,结果是通过递归计算的。
#include <cstddef> // size_t
template< typename T >
struct rank { static constexpr size_t value = 0u; };
template< typename U, size_t N >
struct rank< U[N] > { static constexpr size_t value = 1u + rank< U >::value; };
template< typename U >
struct rank< U[] > { static constexpr size_t value = 1u + rank< U >::value; };
int main() {
using array_t1 = int[10][20][30];
static_assert(rank< array_t1 >::value == 3);
using array_t2 = int[][20];
static_assert(rank< array_t2 >::value == 2);
}
integral_constant就可方便地定义编译期常量,而无需再通过enum和static const变量方式。
#include <type_traits> // std::size_t
template< typename T, T v >
struct integral_constant {
static constexpr T value = v;
constexpr operator T () const noexcept { return value; }
constexpr T operator () () const noexcept { return value; }
};
template< typename T >
struct rank : integral_constant< std::size_t, 0u > {};
template< typename U, std::size_t N >
struct rank< U[N] > : integral_constant< std::size_t, 1u + rank< U >::value > {};
template< typename U >
struct rank< U[] > : integral_constant< std::size_t, 1u + rank< U >::value > {};
int main() {
using array_t = int[][20][30];
static_assert(rank< array_t >::value == 3);
integral_constant< int, 100 > ic;
int c1 = ic; // 用到了 operator T ()
int c2 = ic(); // 用到了 T operator ()
}
返回类型的元函数
#include <type_traits> // std::is_same_v
template< typename T >
struct remove_const { using type = T; };
template< typename T >
struct remove_const< const T > { using type = T; };
int main() {
static_assert(std::is_same_v< remove_const< int >::type, int > == true);
static_assert(std::is_same_v< remove_const< const int >::type, int > == true);
}
定义 type_is 增加可读性
#include <type_traits> // std::is_same_v
template< typename T >
struct type_is { using type = T; };
template < typename T >
struct remove_volatile : type_is< T > {};
template< typename T >
struct remove_volatile< volatile T > : type_is< T > {};
int main() {
static_assert(std::is_same_v< remove_volatile< int >::type, int > == true);
static_assert(std::is_same_v< remove_volatile< volatile int >::type, int > == true);
}
元函数中的条件分支
代码中 true 可以换成一个编译时的表达式,根据配置信息,在编译时改变行为
根据情况选择不同的数据类型
#include <type_traits> // std::is_same_v
template< typename T >
struct type_is { using type = T; };
template< bool, typename T, typename >
struct IF : type_is< T > {};
template< typename T, typename F >
struct IF< false, T, F > : type_is< F > {};
int main()
{
using Type1 = IF<true, int, double>::type;
static_assert(std::is_same_v< Type1, int > == true);
}
根据情况选择不同的基类
#include <type_traits> // std::is_same_v
template< typename T >
struct type_is { using type = T; };
template< bool, typename T, typename >
struct IF : type_is< T > {};
template< typename T, typename F >
struct IF< false, T, F > : type_is< F > {};
struct A { static constexpr int a = 10; };
struct B { static constexpr int b = 20; };
struct C : public IF< true, A, B >::type { static constexpr int c = 30; };
int main() {
C v;
static_assert(v.a == 10);
static_assert(v.c == 30);
}
根据情况执行不同的函数
#include <type_traits> // std::is_same_v
#include <iostream>
template< typename T >
struct type_is { using type = T; };
template< bool, typename T, typename >
struct IF : type_is< T > {};
template< typename T, typename F >
struct IF< false, T, F > : type_is< F > {};
struct F {
void operator()() { std::cout << "F"; }
};
struct G {
void operator()() { std::cout << "G"; }
};
int main()
{
IF< true, F, G >();
// 演讲中的例子代码是 IF< true, F, G > {} ();
// 类似 static_assert(std::is_void<bool>{}() == false);
// 目前还不知道自己做的话,要怎么实现
}
C++11中, 这个 IF 叫 conditional
#include <type_traits> // std::is_same_v
int main() {
using Type1 = std::conditional<true, int, double>::type;
static_assert(std::is_same_v< Type1, int > == true);
}
enable_if 实现原理
定义一个结构,当满足某个条件时,有 type 的定义。如果条件没有满足,那么完全不存在 type 的定义。这样在外部访问 ::type 时,就会失败,从而触发 SFINAE。
template< bool, typename T = void >
struct enable_if : type_is< T > {};
template< typename T >
struct enable_if< false, T > {};
enable_if 使用举例
#include <type_traits> // std::is_same_v
template< typename T >
struct type_is { using type = T; };
template< bool, typename T = void >
struct enable_if : type_is< T > {};
template< typename T >
struct enable_if< false, T > {};
template< typename T >
constexpr typename enable_if< std::is_integral< T >::value, long long >::type
f(T val) { return 1; };
template< typename T >
constexpr typename enable_if< std::is_floating_point< T >::value, long double >::type
f(T val) { return 1; };
int main() {
static_assert(std::is_same_v< decltype(f(1)), long long > == true);
static_assert(std::is_same_v< decltype(f(1.0)), long double > == true);
}
备注信息
访问 ::value 的几种方式
库函数默认支持3,4两种方式。自定义的类,需要写一些代码配合这样的调用方式。
#include <type_traits>
int main() {
static_assert(std::is_void<bool>::value == false); // TR1
static_assert(bool(std::is_void<bool>{}) == false); // C++11
static_assert(std::is_void<bool>{}() == false); // C++14
static_assert(std::is_void_v<bool> == false); // C++17
}