chapter3 去重复: type_traits / 可变参数
2022-06-18 本文已影响0人
my_passion
chapter3 去重复: type_traits / 可变参数模板
编译期 获取 类型信息
1 type_traits
编译期
计算
查询
判断: 检查 是否是 正确类型, 写出 更安全的代码
转换
选择: 一定程度消除 if-else switch-case, 降低 圈复杂度
编译期常量 std::integral_constant
(模板)类型 判断/谓词 std::is_reference
类型转换 std::remove_reference std::decay
编译期 条件选择器 std::conditional
编译期获取 callableObj 的 函数调用运算符 的 return type: std::result_of
2 可变参数模板
C 中 可变参数函数
C++ 可变参数 模板 函数/类
往往和 type_traits 实现更强大的 编译期计算
3.1 type_traits <type_traits>
3.1.1 基本 traits
(1) 编译期常量
`编译期常量` 定义 & 获取
[1] static const 成员
struct A
{
static const int value = 1;
};
A::value
[2] enum 成员
struct A
{
enum {value = 1};
};
A::value
|
| C++11: 不必再额外定义变量 => 放 Base 类定义
|/
[3] 只需从 std::integral_constant 派生
template <typename T>
struct A: std::integral_constant<T, 1> {};
| |
|/ |/
type value
A<int>::value
编译期 true/false 类型
using true_type = std::integral_constant<bool, true>;
using false_type = std::integral_constant<bool, false>;
实现 std::integral_constant
类型 模板参数
namespace std |\ 值 模板参数
{ | /
template<typename T, T v>
struct integral_constant
{
static const T value = v; // value 成员
using value_type = T; // 类型 模板参数 别名
using type = integral_constant<T, v>; // 自身别名
// 类型转换运算符: 使得需要时发生 integral_constant<T, v> 到 value_type 的 隐式转换
operator value_type() { return value; }
};
}
(2) (模板)类型 判断/谓词
继承自 std::std::integral_constant
judge 类型模板参数 是否为 某种类型
std::is_polymorphic / std::is_array / std::is_function / std::is_reference / std::is_const ...
[1]
template<class T>
struct is_polymorphic;
T 是否有 vf
[2] std::is_...::value == true/false
=> 类型模板参数 T 是/否 为 `目标类型`
(3) 2个 (模板)类型间关系
template<class T, class U>
struct is_same;
std::cout << std::is_same<int, signed int>::value; // vs 下: 1
类型严格相同 才认为 类型一致
template<class Base, class Derived>
struct is_base_of;
class B...
class D...
std::is_base_of<B, D>::value
(4) 类型转换: 移除(remove)/添加(add) reference pointer const (const volatile) & 移除数组 顶层(第1)/所有维度 & decay
::type 成员 是输出 类型
[1] template<typaname T>
struct remove_reference;
[2] remove_pointer
[3] remove_const
[4] remove_cv
[5] remove_extent
remove_all_extents
std::cout << std::is_same<int[2], std::remove_extent<int[][2]>::type >::value << std::endl; // vs 下: 1
std::cout << std::is_same<int, std::remove_all_extents<int[][2]>::type >::value << std::endl; // vs 下: 1
[5] decay
template<typename T>
...std::decay<T>
1] 移除 T 的引用 -> U
using U = std::remove_reference<T>::type
2] if U 为 数组: std::is_array<U>::value == true, 去 顶层维度, 加指针
remove_extent<U>::type*
3] else if U 为 函数: std::is_function<U>::value == true, add_pointer
add_pointer<U>::type
4] else, remove_cv<U>::type
using T1 = typename std::decay<int[2]>::type; // int*
using T2 = typename std::decay<int(int)>::type; // int(*)(int)
using T3 = typename std::decay<int&&>::type; // int
using T4 = typename std::decay<const int&>::type; // int
// ===1. remove/add reference
创建 智能指针: 模板参数 T 的 原始类型 U
std::remove_reference 移除 T 可能的引用
获取 智能指针 所指对象(的引用)
增加左值引用
#include <iostream>
#include <type_traits>
#include <memory>
template <typename T>
class Sp
{
public:
using U = typename std::remove_reference<T>::type;
private:
std::unique_ptr<U> uptr;
public:
Sp() : uptr(new U(1) ) {}
typename std::add_lvalue_reference<U>::type
get() const
{
return *uptr.get();
}
};
int main()
{
Sp<int> sp;
int a = sp.get();
std::cout << a << std::endl; // 1
}
// ===2. decay 的引入
简化 remove_reference + remove_cv
创建 模板参数 T 的 原始类型 U 的 heap 对象
T cv 的 引用 类型 => 直接用 new T() 编译报错
template<typename T>
T* create()
{
return new T();
}
int* p = create<const volatile int&>();
|
| 解决
|/
remove_reference + remove_cv
#include <iostream>
#include <type_traits>
#include <memory>
template<typename T>
typename std::remove_cv< typename std::remove_reference<T>::type >::type*
create()
{
using U = typename std::remove_cv< typename
std::remove_reference<T>::type >::type;
return new U();
}
int main()
{
int* p = create<const volatile int&>();
}
|
| 简化 remove_reference + remove_cv
|/
#include <iostream>
#include <type_traits>
#include <memory>
template<typename T>
typename std::decay<T>::type*
create()
{
using U = typename std::decay<T>::type;
return new U();
}
int main()
{
int* p = create<const volatile int&>();
}
// ===3. decay 用于函数
将 函数(函数类型 无法保存) 变为 函数指针 -> 保存 函数指针 -> 延迟执行
#include <iostream>
#include <type_traits>
template<typename F>
struct FuncWrapper
{
using FT = typename std::decay<F>::type;
FT pf;
FuncWrapper(F& f_) : pf(f_)
{
}
void call() { pf(); }
};
void f() { std::cout << "hello\n"; }
using FuncType = void();
int main()
{
// 函数名 被编译器视为 函数指针, f 类型: void(*)()
FuncWrapper<FuncType> fw(f);
fw.call();
}
3.1.2 编译期 条件选择器: std::conditional
据 判断式 B == true/false 从 2个类型 T1, T2 中 选 1个 T1/T2
template<bool B, typename T1, typename T2>
std::conditional;
using T = std::conditional<std::is_const<const int>::value, long, int>::type; // long
3.1.3 编译期获取 callableObj 的 函数调用运算符 的 return type: std::result_of
(1) 引入
编译时推断 获取 callableObj 的 函数调用运算符(表达式) 的 return type
Note: 不真正调用 函数调用运算符
[1] auto + decltype
0 -> 强转为 T ptr (T*)0 -> 解引用 *(T*)0 得 T 对象: undefined value
template<typename F, typename Arg>
decltype( ( *(F*)0 )( *(Arg*)0 ) )
Func(F f, Arg arg)
{
return f(arg);
}
[2] 后置返回类型 auto + decltype
template<typename F, typename Arg>
auto Func(F f, Arg arg) -> decltype( f(arg) )
{
return f(arg);
}
问题
[1] F 无 模板参数 时, 不能用 decltype( f(arg) ) 获取类型
[2] F 无 默认 Ctor 时, 不能用 F() 推导 F 成员函数的 return type
#include <iostream>
#include <type_traits> // std::result_of
#include <utility> // std::declval
class F
{
F() = delete;
public:
int operator()(int i)
{
return i;
}
int foo() const { return 1; }
};
template<typename F, typename Arg>
auto Func(F f, Arg arg) -> decltype(f(arg))
{
return f(arg);
}
int main()
{
// decltype( F()(1) ) i = 2; // F() 导致 编译报错
decltype(std::declval<F>()(std::declval<int>())) i = 2;
decltype(std::declval<F>().foo()) j = 3;
std::result_of<F(int)>::type k = 5;
}
[3] std::declval
思路
decltye( std::declval<F>()(std::declval<int>() ) ) i = 3;
std::declval<F/int>()
获取 任意类型 (F/int) 的 临时值 引用
Cannot be called => never returns a value
decltype: `不求值, 只推断 expr 返回类型`
decltye( std::declval<F>()( std::declval<int>() ) )
|
| 等价于
|/
std::result_of<F(int)>::type
[4] std::result_of
template<typename CallableObj, typename... ArgTypes>
class result_of< CallableObj(ArgTypes...) >;
someCallableObj::operator()(someArgTypes...)
|\
|
std::result_of< someCallableObj(someArgTypes...) >::type
#include <iostream>
#include <type_traits> // std::result_of
int f(int) { return 1; }
int main()
{
using fp = int(*)(int); // 函数指针 类型
using T1 = std::result_of<decltype(f)& (int)>::type; // T1 == int
using T2 = std::result_of<fp(int)>::type; // T2 == int
T1 i;
T2 j;
}
3.2 可变参数模板
3.2.0 C 中 可变参数函数
2点可变
参数 数量可变
参数 类型可变
实现: <stdarg.h> 4 个 宏
va_list 存储可变参数信息
va_arg 获取 next 参数, 并自动后移1个位置
va_start 开始使用 可变参数列表
va_end 结束使用 可变参数列表
#include <stdarg.h>
#include <stdio.h>
double sum(int n, ...)
{
double sum = 0.0;
va_list ptr;
va_start(ptr, n);
for (int i = 0; i < n; i++)
{
sum += va_arg(ptr, double); // 指定 形参类型
}
va_end(ptr);
return sum;
}
int main(int argc, char** argv)
{
printf("%lf\n", sum(3, 1.0, 2.0, 3.0) );
}
局限: 程序中必须 显式告诉 compiler 可变参数 个数/类型。 用 第1实参 告诉 个数, other 实参 与 指定形参类型 去匹配
C++ 可变参模板 怎么做到 不告诉 compiler 参数个数 和 参数类型
?依赖于
[1] 函数重载
, 依靠参数 的 pattern 去 匹配
对应函数
[2] 函数模板 实参推断
[3] 类模板, 基于 偏特化
(partial specialization) 来选择不同的实现
typename... Args: template parameter pack, 表明有多种 type
Args... tail: function parameter pack, 表明有多个参数
tail...: pack expansion
#include <cstdarg>
#include <iostream>
template<typename T>
double sum(T t)
{
return t;
}
template<typename T, typename ... Args>
double sum(T t, Args... tail)
{
return sum(t) + sum(tail...);
}
int main(int argc, char** argv)
{
std::cout << sum(1.0, 2.0, 3.0) << std::endl;
}
3.2.1 可变参数 模板函数
(1) 递归方式 展开参数包
参数包 Args... 展开过程中 递归调用,
每 调用1次 参数包中参数少1个, 直到所有参数都展开
#include <iostream>
// 递归终止函数: 可以有多种版本
// === version1
void print()
{
std::cout << "empty\n";
}
// 参数包展开函数
template <typename T, typename ...Args>
void print(T t, Args... tail)
{
std::cout << t << std::endl;
print(tail...);
}
int main()
{
print(1, 2.3, "hello");
}
递归调用过程
print(1, 2.3, "hello");
print(2.3, "hello");
print("hello");
print();
// === version2
template <typename T>
void print(T t)
{
std::cout << t << std::endl;
}
递归调用过程
print(1, 2.3, "hello");
print(2.3, "hello");
print("hello");
(2) 应用
可变参数的 工厂函数
#include <iostream>
class A
{
public:
A(int) {}
};
class B
{
public:
B(int, double) {}
};
template<typename T, typename... Args>
T* getInstance(Args&&... args)
{
return new T(std::forward<Args>(args)...);
}
int main()
{
A* pa = getInstance<A>(1);
B* pb = getInstance<B>(1, 2.0);
}
3.2.1 可变参数 模板类
编译期计算 参数包 中 参数类型的 size 和
#include <iostream>
// 前向声明
template<typename... Args> struct A;
// 特化的递归终止类
template<typename Last>
struct A<Last>
{
enum { value = sizeof(Last) };
};
// 部分展开的 可变参数模板类
template<typename First, typename... Tails>
struct A<First, Tails...>
{
enum { value = A< First>::value + A<Tails...>::value };
};
int main()
{
std::cout << A<int, double>::value << std::endl;
}