{ C++ 模板元编程笔记 } 基本原理

本文详细介绍了C++元编程中的关键概念,如元函数的返回值处理、递归应用在类型计算上,以及如何利用enable_if实现条件编译。通过实例展示了如何使用integral_constant和IF结构进行类型选择。
摘要由CSDN通过智能技术生成

基于 Walter E. Brown 的演讲 - Modern Template Metaprogramming (1)
示例代码可以贴到 www.onlinegdb.com 直接运行,需要选择C++17

总结的内容

  1. 元编程中最主要的两个变量就是 ::value 和 ::type
  2. 递归是通过偏特化定义终止条件来实现的

返回值的元函数

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
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值