IE盒子

搜索
查看: 136|回复: 2

C++模板元编程 Type Traits (一)

[复制链接]

5

主题

9

帖子

23

积分

新手上路

Rank: 1

积分
23
发表于 2023-1-7 19:32:49 | 显示全部楼层 |阅读模式
Metafunctions 元函数

传统的函数零个或者多个参数,并且有一个返回值或者void。例如:
void do_something();
int do_something_else(int, char const*);
函数的返回机制是碰到"return"关键字就会返回一个值。
int do_something_else(int, char const*) {
    return 42;
}
一个元函数(metafunction)不是一个函数,而是一个class/struct。元函数不是语言本身的部分,也没有正式的语言支持,它们作为现有语言的惯用法而存在,并且C++社区已经形成了通用的“标准”约定。
元函数在技术上的实现方式,通常为:一个带有零个或者多个模板参数的class,并且其class内部定义了零个或者多个types和values作为返回值。
从元函数返回value/type

1、定义一个public value,作为元函数的返回值:
template <typename T>
struct TheAnswer {
    static constexpr int value = 42;
};
2、定义一个public type,作为元函数的返回type:
template <typename T>
struct Echo {
    using type = T;
};
Value Metafunctions

简单的常规函数identity:
int int_identity(int x) {
    return x;
}
assert(42 == int_identity(42));
简单的元函数identity:
template <int X>
struct IntIdentity {
    static constexpr int value = X;
}
static_assert(42 == IntIdentity<42>::value);
泛型版的常规函数identity:
template <typename T>
T identity(T x) {
    return x;
}

// Returned type will be int
assert(42 == identity(42));
// Returned type will be unsigned long long
assert(42ull == identity(42ull));
泛型版的元函数identity:
template <typename T, T Value>
struct ValueIdentity {
    static constexpr T value = Value;
}

// The type of value will be int
static_assert(42 == ValueIdentity<int, 42>::value);
// The type of value will be unsigned long long
static_assert(ValueIdentity<unsigned long long, 42ull>::value == 42ull);
泛型版的元函数identity(C++ 17):
template <auto X>
struct ValueIdentity {
    static constexpr auto value = X;
}

// The type of value will be int
static_assert(42 == ValueIdentity<42>::value);
// The type of value will be unsigned long long
static_assert(42ull == ValueIdentity<42ull>::value);
简单的常规函数和元函数sum:
int sum(int x, int y) {
    return x + y;
}

template <int X, int Y>
struct IntSum {
    static constexpr int value = X + Y;
};
static_assert(42 == IntSum<30, 12>::value);
泛型版的常规函数和元函数sum,并且常规函数加了constexpr关键字,也可以用于编译期求值:
template <typename X, typename Y>
constexpr auto sum(X x, Y y) {
    return x + y;
}

// Return type will be unsigned long long
static_assert(42ull == sum(30, 12ull));

template <auto X, auto Y>
struct Sum {
    static constexpr auto value = X + Y;
};

// Return type will be unsigned long long
static_assert(42ull == Sum<30, 12ull>::value);
Type Metafunctions

随着constexpr的出现,type metafunction变得非常有用。其在类内部声明一个type,来作为返回值。
template <typename T>
struct TypeIdentity {
    using type = T;
};
调用元函数

1、元函数的调用
调用value metafunction:ValueIdentity<42>::value;
调用type metafunction:typename TypeIdentity<int>::type
2、简化元函数的调用
value metafunction通过使用带有“_v”后缀的非类型参数模板来简化调用:
template <auto X>
inline constexpr auto ValueIdentity_v = ValueIdentity<X>::value;

static_assert(42 == ValueIdentity<42>::value);
static_assert(42 == ValueIdentity_v<42>);
type metafunction通过使用带有“_t”后缀的别名模板来简化调用:
template <typename T>
using TypeIdentity_t = typename TypeIdentity<T>::type;

static_assert(std::is_same_v<int,TypeIdentity_t<int>>);
常见的元函数剖析

std::integral_constant

一个非常有用的元函数
template <class T, T v>
struct integral_constant {
    static constexpr T value = v;
   
    using value_type = T;
    using type = integral_constant<T, v>;
   
    constexpr operator value_type() const noexcept {
        return value;
    }
    constexpr value_type operator()() const noexcept {
        return value;
    }
};
std::bool_constant

同样是一个非常方便的元函数
template <bool B>
using bool_constant = integral_constant<bool, B>;

using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
true_type和false_type被称为空元函数,因为它们没有形参。
true_type::value
false_type::value
type traits分类

一元的type traits
具有的特点:

  • 类模板
  • 一个模板类型参数(通常情况下)
  • 默认构造函数(C++ 17)
  • 拷贝构造函数(C++ 17)
  • 公开的继承于特化的std::integral_constant
二元的type traits
具有的特点:

  • 类模板
  • 两个模板类型参数(通常情况下)
  • 默认构造函数(C++ 17)
  • 拷贝构造函数(C++ 17)
  • 公开的继承于特化的std::integral_constant
Transformation(变换)的type traits
具有的特点:

  • 类模板
  • 一个模板类型参数(通常情况下)
  • 类内部定义了一个公开嵌套的type,其名字为“type”
  • 没有默认的构造或者拷贝构造函数的需求
  • 没有继承需求
is_void(一元)

当类型是void时,应为true_type;否则为false_type。
template <typename T>
struct is_void : std::false_type { };

template <>
struct is_void<void> : std::true_type { };

static_assert(is_void<void>{});
static_assert(not is_void<int>{});
如何考虑const和volatile限定符:类型T和具有cv限定符的类型T应用于该模板时,应该产生相同的结果。
template <typename T> struct is_void : std::false_type { };
template <> struct is_void<void> : std::true_type { };
template <> struct is_void<void const> : std::true_type { };
template <> struct is_void<void volatile> : std::true_type { };
template <> struct is_void<void const volatile> : std::true_type { };

// The standard mandates this as well...
template <typename T>
inline constexpr bool is_void_v = is_void<T>::value;
remove_const(Transformation)

通过该元函数将top-level的const限定符删除。
template <typename T>
struct TypeIdentity {
    using type = T;
};

// Primary template, do nothing if no const
template <typename T>
struct remove_const : TypeIdentity<T> { };
// Partial specialization, when detect const

template <typename T>
struct remove_const<T const> : TypeIdentity<T> { };

// Standard mandated convenience alias
template <typename T>
using remove_const_t = typename remove_const <T>::type;
使用如下:
remove_const<int> -> int
remove_const<const int> -> int
remove_const<const volatile int> -> volatile int
remove_const<int *> -> int *
remove_const<const int *> -> const int *
remove_const<int const *> -> int const *
remove_const<int const * const> -> int const *
remove_const<int * const> -> int *
conditional(Transformation)

template <bool Condition, typename T, typename F>
struct conditional : TypeIdentity<T> { };

template <typename T, typename F>
struct conditional<false, T, F> : TypeIdentity<F> { };

template <bool Condition, typename T, typename F>
using conditional_t = typename conditional<Condition, T, F>::type;

static_assert(is_same_v<int, conditional_t<is_void<void>::value, int, long>);
static_assert(is_same_v<long, conditional_t<is_void<char>::value, int, long>);
参考

https://www.youtube.com/watch?v=tiAVWcjIF6o
本文使用 Zhihu On VSCode 创作并发布
回复

使用道具 举报

1

主题

7

帖子

8

积分

新手上路

Rank: 1

积分
8
发表于 2023-1-7 19:33:09 | 显示全部楼层
[捂脸]看着你的元函数,宏函数说:“你忘了我,那我也抛弃你。”模板说:“我都轻易不想这么用。”constexpr function心里暗骂:“真他*的。”immediate function(立即函数)脱口大骂:“****!”看着你的type traits,type_traits库本库表示:“你再好好看看我。”std::condition表示很高兴:“居然有人还记得我。”但随后和std::remove_const看着自己实现中的TypeIdentity挠了挠头。看着std::condition,deduction guides(推断导引)说:“遇到困难别找我。”文章也不能说毫无意义。
回复

使用道具 举报

3

主题

14

帖子

23

积分

新手上路

Rank: 1

积分
23
发表于 2025-5-12 18:37:09 | 显示全部楼层
路过的帮顶
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表