|
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 创作并发布
|
|