博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

C++ 模板元编程 笔记


很有意思但不知道实际有啥用的东西

链表

#include <iostream>
#include <type_traits>

/*
功能:
- 获取 size
- 将 List 从指定位置拆分成两个
- 合并两个 List
- 读取/删除某位置的元素
- 在头部/末尾/任意位置插入元素
- 查询某元素第一次出现的位置
(修改指定位置的值,可以通过删除+插入实现,懒得再封装了)

可以支持任意类型而不是 int,再加一个模板参数 T 即可。
*/
template <int... values>
struct List;

template <>
struct List<> {
	static constexpr size_t size = 0;
};
using EmptyList = List<>;

template <int head, int... values>
struct List<head, values...> {
	static constexpr int front = head;
	static constexpr size_t size = 1 + sizeof...(values);
	using PopFrontT = std::conditional_t<size == 1, EmptyList, List<values...>>;
};

// template <int head>
// struct List<head> { // 长度为 1 的情况
// 	static constexpr int front = head;
// 	static constexpr size_t size = 1;
// 	using PopFrontT = EmptyList;
// };

// --- insert
template <typename T, int value>
struct PushFront;

template <int... values, int x>
struct PushFront<List<values...>, x> {
	using type = List<x, values...>;
};

template <typename T, int value>
struct PushBack;

template <int... values, int x>
struct PushBack<List<values...>, x> {
	using type = List<values..., x>;
};

// --- split:将 List 从指定位置拆分成两个
template <size_t pos, typename L, typename R>
struct SplitImpl;

template <size_t now, int... L, int... R>
struct SplitImpl<now, List<L...>, List<R...>> { // 每次取出 R 中的一个放入 L
	using next = SplitImpl<now - 1, List<L..., List<R...>::front>, typename List<R...>::PopFrontT>;
	using newL = typename next::newL;
	using newR = typename next::newR;
};

template <int... L, int... R>
struct SplitImpl<0, List<L...>, List<R...>> {
	using newL = List<L...>;
	using newR = List<R...>;
};

template <size_t pos, typename T>
struct Split;

template <size_t pos, int... values>
struct Split<pos, List<values...>> {
	using L = typename SplitImpl<pos, EmptyList, List<values...>>::newL;
	using R = typename SplitImpl<pos, EmptyList, List<values...>>::newR;
};

// --- merge:合并两个 List
template <typename L, typename R>
struct Merge;

template <int... L, int... R>
struct Merge<List<L...>, List<R...>> {
	using type = List<L..., R...>;
};

// --- get:读取某位置的元素
template <size_t pos, typename T>
struct Get;

template <size_t pos, int... values>
struct Get<pos, List<values...>> {
	static constexpr int value = Get<pos - 1, typename List<values...>::PopFrontT>::value;
};

template <int... values>
struct Get<0, List<values...>> {
	static constexpr int value = List<values...>::front;
};

// --- delete:删除某位置的元素
template <size_t pos, typename T>
struct Delete;

template <size_t pos, int... values>
struct Delete<pos, List<values...>> {
	using temp = Split<pos, List<values...>>;
	using type = typename Merge<typename temp::L, typename temp::R::PopFrontT>::type;
};

// --- insert:在某位置插入元素
template <size_t pos, int x, typename T>
struct Insert;

template <size_t pos, int x, int... values>
struct Insert<pos, x, List<values...>> {
	using temp = Split<pos, List<values...>>;
	using type = typename Merge<typename PushBack<typename temp::L, x>::type, typename temp::R>::type;
};

// --- find:查询某元素第一次出现的位置
template <typename T, int v, int now>
struct FindImpl;

template <int v, int front, int... values, int now>
struct FindImpl<List<front, values...>, v, now> {
	static constexpr int pos = v == front ? now : FindImpl<List<values...>, v, now + 1>::pos;
};

template <int v, int now>
struct FindImpl<List<>, v, now> {
	static constexpr int pos = -1;
};

template <typename T, auto value>
struct Find;

template <int v, int... values>
struct Find<List<values...>, v>: FindImpl<List<values...>, v, 0> {};

// ---

int main() {
	using L1 = List<1, 2, 3, 4>;
	using L1l = Split<2, L1>::L;
	using L2 = List<3, 4, 5>;
	using L1l2 = Merge<L1l, L2>::type;
	using L2pf = PushFront<L2, 2>::type;
	using L2pfpb = PushBack<L2pf, 6>::type;

	using L2d1 = Delete<0, L2pfpb>::type;
	using L2d2 = Delete<2, L2pfpb>::type;
	using L2d3 = Delete<4, L2pfpb>::type;

	using L3i1 = Insert<0, 0, L1>::type;
	using L3i2 = Insert<2, 0, L1>::type;
	using L3i3 = Insert<4, 0, L1>::type;

	[](...) {} (
		EmptyList::size,
		L1::size,

		Get<0, L1>::value,
		Get<3, L1>::value,
		// Get<4, L1>::value, // 可以在编译期检查越界(因为没有该模板的定义)

		Find<L1, 1>::pos,
		Find<L1, 3>::pos,
		Find<L1, 4>::pos,
		Find<L1, 5>::pos
	);
}

数组

#include <cstdio>
#include <vector>
#include <iostream>
#include <type_traits>

/*
功能:
- 获取 size
- 拆分一个数组
- 合并两个数组
- 读写某位置的元素
- 在任意位置插入元素

实现与链表类似,只是有数组后可直接获取某位置的元素。
支持任意类型(只能有一种,也可以用 auto 和 common_type 实现而不是参数 T)。
template<auto... items>
struct Array {
    using type = std::common_type_t<decltype(items)...>;
    static constexpr type value[sizeof...(items)] = {items...};
};
*/

template<typename T, T... values>
struct Array;

template<typename T, T head, T... values>
struct Array<T, head, values...> {
	using ValueType = T;
	static constexpr size_t size = 1 + sizeof...(values);
	static constexpr T front = head;
	static constexpr T data[] = {head, values...};
	using PopFrontT = Array<T, values...>;

	static_assert(size > 0);
};

template<typename T>
struct Array<T> {
	using ValueType = T;
	static constexpr size_t size = 0;
};

template<typename T>
using EmptyArray = Array<T>;

// split
template <size_t pos, typename L, typename R>
struct SplitImpl;

template <size_t now, typename T, T... L, T... R>
struct SplitImpl<now, Array<T, L...>, Array<T, R...>> {
	using next = SplitImpl<now - 1, Array<T, L..., Array<T, R...>::front>, typename Array<T, R...>::PopFrontT>;
	using newL = typename next::newL;
	using newR = typename next::newR;
};

template <typename T, T... L, T... R>
struct SplitImpl<0, Array<T, L...>, Array<T, R...>> {
	using newL = Array<T, L...>;
	using newR = Array<T, R...>;
};

template <size_t pos, typename T>
struct Split;

template <size_t pos, typename T, int... values>
struct Split<pos, Array<T, values...>> {
	using L = typename SplitImpl<pos, EmptyArray<T>, Array<T, values...>>::newL;
	using R = typename SplitImpl<pos, EmptyArray<T>, Array<T, values...>>::newR;
};

// merge:合并两个 Array
template <typename L, typename R>
struct Merge;

template <typename T, T... L, T... R>
struct Merge<Array<T, L...>, Array<T, R...>> {
	using type = Array<T, L..., R...>;
};

// get:读取某位置的元素
template <size_t pos, typename T>
struct Get;

template <size_t pos, typename T, T... values>
struct Get<pos, Array<T, values...>> {
	static constexpr T value = Array<T, values...>::data[pos];
};

// 其它同链表

int main() {
	using A1 = Array<int, 1, 2, 3, 4>;
	using A1l = Split<2, A1>::L;
	using A2 = Array<int, 3, 4, 5>;
	using A1l2 = Merge<A1l, A2>::type;

	[](...) {} (
		EmptyArray<int>::size,
		A1::size,

		Get<0, A1>::value,
		Get<3, A1>::value
		// Get<4, L1>::value // 可以在编译期检查越界(因为没有该模板的定义)
	);
}

归并排序

#include <iostream>
#include <type_traits>

template <int... values>
struct List;

template <>
struct List<> {
	static constexpr size_t size = 0;
};
using EmptyList = List<>;

template <int head, int... values>
struct List<head, values...> {
	static constexpr int front = head;
	static constexpr size_t size = 1 + sizeof...(values);
	using PopFrontT = std::conditional_t<size == 1, EmptyList, List<values...>>;
};

// --- insert
template <typename T, int value>
struct PushFront;

template <int... values, int x>
struct PushFront<List<values...>, x> {
	using type = List<x, values...>;
};

// --- split:将 List 从指定位置拆分成两个
template <size_t pos, typename L, typename R>
struct SplitImpl;

template <size_t now, int... L, int... R>
struct SplitImpl<now, List<L...>, List<R...>> { // 每次取出 R 中的一个放入 L
	using next = SplitImpl<now - 1, List<L..., List<R...>::front>, typename List<R...>::PopFrontT>;
	using newL = typename next::newL;
	using newR = typename next::newR;
};

template <int... L, int... R>
struct SplitImpl<0, List<L...>, List<R...>> {
	using newL = List<L...>;
	using newR = List<R...>;
};

template <size_t pos, typename T>
struct Split;

template <size_t pos, int... values>
struct Split<pos, List<values...>> {
	using L = typename SplitImpl<pos, EmptyList, List<values...>>::newL;
	using R = typename SplitImpl<pos, EmptyList, List<values...>>::newR;
};

// --- merge:合并两个 List
// template <typename L, typename R>
// struct Merge;

// template <int... L, int... R>
// struct Merge<List<L...>, List<R...>> {
// 	using type = List<L..., R...>;
// };

// --- merge:合并两个有序链表
template <typename L, typename R>
struct Merge;

template <int... L, int... R>
struct Merge<List<L...>, List<R...>> {
	using l = List<L...>;
	using r = List<R...>;
	static constexpr int min = l::front < r::front ? l::front : r::front;
	using temp = std::conditional_t<
		l::front < r::front,
		typename Merge<typename l::PopFrontT, r>::type,
		typename Merge<l, typename r::PopFrontT>::type
	>;
	using type = typename PushFront<temp, min>::type; // 每次取出较小值,然后递归处理后面的序列
};

// 递归边界
template <int... R>
struct Merge<EmptyList, List<R...>> {
	using type = List<R...>;
};

template <int... L>
struct Merge<List<L...>, EmptyList> {
	using type = List<L...>;
};

// --- mergeSort:归并排序:每次将链表拆分,递归排序,然后合并
template <typename L>
struct MergeSort;

template <int... values>
struct MergeSort<List<values...>> {
	using list = List<values...>;
	using l = Split<list::size / 2, list>::L;
	using r = Split<list::size / 2, list>::R;
	using sl = MergeSort<l>::type;
	using sr = MergeSort<r>::type;
	using type = Merge<sl, sr>::type;
};

// 模板递归需要定义边界!否则 MergeSort<List<values...>> 定义完之前无法递归使用 MergeSort<List<>> 和 MergeSort<List<v>>
template <int v>
struct MergeSort<List<v>> {
	using type = List<v>;
};

template <>
struct MergeSort<List<>> {
	using type = EmptyList;
};

// ---

int main() {
	using L1 = List<1, 4, 2, 3>;
	using L1l = Split<2, L1>::L;
	using L2 = List<2, 3, 4, 5>;
	using L1l2 = Merge<L1l, L2>::type;
	using L2pf = PushFront<L2, 2>::type;

	using S1 = Split<0, EmptyList>::L;
	using S2 = Split<0, EmptyList>::R;

	using R1 = MergeSort<EmptyList>::type;
	using R2 = MergeSort<List<3>>::type;

	using L3 = List<5, 2, 1, 9, 4, 2, 3, 0, 6>;
	using L3r = MergeSort<L3>::type;

	[](...) {} (
		EmptyList::size,
		L1::size,
		L3::size,
		L3r::size
	);
}

std::variant

#include <vector>
#include <cstring>
#include <cassert>
#include <variant>
#include <iostream>
#include <type_traits>

using std::cout;

/*
[variant](https://zh.cppreference.com/w/cpp/utility/variant)
- 切换类型时析构旧对象、创建新对象。
- 接口:get、emplace、swap。错误时抛出异常。
- 辅助类/函数:get、get_if、holds_alternative、variant_alternative、visit。
- 同一类型可以出现多次,但此时只能用下标访问。

基本实现就是利用 char 数组作为存储,其大小为各类型大小的最大值。在每次切换类型时析构并重新创建对象。
需要维护当前的类型 type,来验证读取的有效性(不能读取非上次写入的类型)。因为不能将类型作为变量,所以 type 是 int 下标。
析构时,需要根据 type 确定当前类型 T,以便将 data 转为 T* 然后调用 ~T()。可以用将每种类型的 Destroy 函数保存在一个函数指针数组里,用下标调用。
注意,下标到类型的转换、类型到下标的转换,都是只能在编译期进行的。type 是运行期变量,所以无法用于这些转换,必须手动指定或根据参数值推导出 T 或下标。

注意:
- data 要对齐。
- reinterpret_cast 本身不会隐式创建对象。切换时需要使用 placement new 构造。
- 由于指针可互转换的要求,即使 data 位置有新的对象,直接 reinterpret_cast 转换 data 访问也是 UB?需要 launder 或使用 placement new 返回的指针。
- 使用 T 初始化时应用 decay(T) 匹配。否则 "abc" *只会*直接匹配 const char&[4],然后才尝试隐式转换到 string 与 const char*(且按顺序选择),无法匹配 const char[4]。但是不需要支持保存数组,只存指针即可。
- 构造时应使用 T(...) 而非 T{...},对于有初始化列表构造函数的类(如 string、vector)会导致不同的结果。

未完善:
- Variant 在多种异常情况下会变得无值,通过 valueless_by_exception 检测。会影响多种函数的执行。
- Variant<float, long, double> z = 0; 应匹配 long,且 int 始终不会匹配浮点、char、short。但不知道怎么实现哪个更匹配,所以目前隐式转换会匹配第一个合适的类型,即使有多个或更合适的。
*/

// --- 获取类型最大大小与对齐
template <typename T, typename... Ts>
struct MaxSize {
	static constexpr size_t value = sizeof(T) >= MaxSize<Ts...>::value ? sizeof(T) : MaxSize<Ts...>::value;
};

template <typename T>
struct MaxSize<T> {
	static constexpr size_t value = sizeof(T);
};

// 其实不需要,alignas(T...) 也可展开。对象会取最大的对齐值
template <typename T, typename... Ts>
struct MaxAlign {
	static constexpr size_t value = alignof(T) >= MaxAlign<Ts...>::value ? alignof(T) : MaxAlign<Ts...>::value;
};

template <typename T>
struct MaxAlign<T> {
	static constexpr size_t value = alignof(T);
};

// --- 下标到类型的转换
template <size_t pos, typename T, typename... Ts>
struct IdToType {
	static_assert(pos < 1 + sizeof...(Ts));
	using type = typename IdToType<pos - 1, Ts...>::type;
};

template <typename T, typename... Ts>
struct IdToType<0, T, Ts...> {
	using type = T;
};

// --- 类型到下标的转换
template <size_t pos, typename X, typename T, typename... Ts>
struct TypeToIdImpl {
	static constexpr size_t value = std::is_same_v<X, T> ? pos : TypeToIdImpl<pos + 1, X, Ts...>::value;
};

template <size_t pos, typename X, typename T>
struct TypeToIdImpl<pos, X, T> {
	static constexpr size_t value = std::is_same_v<X, T> ? pos : (size_t)-1;
};

template <typename X, typename... Ts>
struct TypeToId {
	static constexpr size_t value = TypeToIdImpl<0, X, Ts...>::value;
};

// --- 类型到下标的转换,但允许隐式转换,类型不必完全相同
template <size_t pos, typename X, typename T, typename... Ts>
struct ConvertTypeToIdImpl {
	static constexpr size_t value = std::is_convertible_v<X, T> ? pos : ConvertTypeToIdImpl<pos + 1, X, Ts...>::value;
};

template <size_t pos, typename X, typename T>
struct ConvertTypeToIdImpl<pos, X, T> {
	static constexpr size_t value = std::is_convertible_v<X, T> ? pos : (size_t)-1;
};

template <typename X, typename... Ts>
struct ConvertTypeToId {
	static constexpr size_t value = ConvertTypeToIdImpl<0, X, Ts...>::value;
};

// --- utility
// 计算类型出现数量
template <typename X, typename T, typename... Ts>
struct CountType {
	static constexpr size_t value = (std::is_same_v<X, T> ? 1 : 0) + CountType<X, Ts...>::value;
};

template <typename X, typename T>
struct CountType<X, T> {
	static constexpr size_t value = (std::is_same_v<X, T> ? 1 : 0);
};

// 判断 V 是否是模板 T 的实例化
template <template<typename...> class T, typename V> // T 拥有模板参数 typename...
struct T_is_instantiation_of: std::false_type {};

template <template<typename...> class T, typename... Ts>
struct T_is_instantiation_of<T, T<Ts...>>: std::true_type {};

template <template<size_t...> class T, typename V> // T 拥有模板参数 size_t...
struct V_is_instantiation_of: std::false_type {};

template <template<size_t...> class T, size_t... Ts>
struct V_is_instantiation_of<T, T<Ts...>>: std::true_type {};

// --- [消歧义标签](https://zh.cppreference.com/w/cpp/utility/in_place)
// 构造 Variant 时,需传递 std::in_place_type<T> 和 T 的构造参数,指明要使用的类型是 T(也可指明下标)
template <class T>
struct in_place_type_t {
    explicit in_place_type_t() = default;
};
template <class T>
inline constexpr in_place_type_t<T> in_place_type{};

template <std::size_t I>
struct in_place_index_t {
    explicit in_place_index_t() = default;
};
template <std::size_t I>
inline constexpr in_place_index_t<I> in_place_index{};

// --- 其它
template <typename... Ts>
struct Variant;

template <class T>
static void DestroyImpl(unsigned char* data) {
	if constexpr (!std::is_trivially_destructible_v<T>) {
		std::launder(reinterpret_cast<T*>(data))->~T();
	}
}

// --- variant_alternative:将下标转为类型(获取当前选项)。如果 variant 有 const 限定则传给类型。
template <size_t I, class T>
struct variant_alternative;

template <std::size_t I, class... Ts>
struct variant_alternative<I, Variant<Ts...>> {
	static_assert(I < sizeof...(Ts));
	using type = IdToType<I, Ts...>::type;
};

template <std::size_t I, class... Ts>
class variant_alternative<I, const Variant<Ts...>> {
	static_assert(I < sizeof...(Ts));
	using type = std::add_const_t<typename IdToType<I, Ts...>::type>;
};

template <size_t I, class T>
using variant_alternative_t = variant_alternative<I, T>::type;

template <class T, class... Ts>
constexpr bool holds_alternative(const Variant<Ts...>& v) noexcept {
	try {
		v.template get<T>();
	} catch (const std::bad_variant_access&) {
		return false;
	}
	return true;
}

// --- visit
template <typename F, typename V, size_t index>
static auto visitImpl(F&& func, V& var) {
	return func(var.template get<index>());
}

template <typename F, typename V>
auto visit(F&& func, V&& var)
	requires T_is_instantiation_of<Variant, std::decay_t<V>>::value
{
	return var.visit(std::forward<F>(func));
}

// --- variant
static constexpr size_t variant_npos = -1;

template <typename... Ts>
struct Variant {
	static_assert(sizeof...(Ts) > 0,
		"variant must have at least one alternative");
	static_assert(!(std::is_reference_v<Ts> || ...),
		"variant must have no reference alternative");
	static_assert(!(std::is_void_v<Ts> || ...),
		"variant must have no void alternative");

	using T0 = IdToType<0, Ts...>::type;

	constexpr Variant()
		noexcept(std::is_nothrow_default_constructible_v<T0>)
		requires std::is_default_constructible_v<T0>
	{
		type = 0;
		new (data) T0{};
	}
	/*
		没有办法在运行时得知当前 Variant 保存的类型,所以无法实现这两个构造函数。
		只有像下面的函数一样,将参数写为模板参数(编译期常量)才可以。
		将 Ts... 和当前的 type_info::name 作为 const char* 保存,可以实时判断当前的 typeid,但是没法把它转换回类型。
	*/
	Variant(const Variant& other) = delete;
	Variant(Variant&& other) = delete;
	// constexpr Variant(const Variant& other)
	// 	requires (std::is_copy_constructible_v<Ts> && ...)
	// {
	// 	type = other.type;
	// 	using T = IdToType<type, Ts...>::type; // error:type 不是编译时常量,不能用于编译期的类型获取
	// 	new (data) T(other.get<type>());
	// }
	// constexpr Variant(Variant&& other)
	//	requires (std::is_move_constructible_v<Ts> && ...)
	// {
	// 	type = other.type;
	// 	using T = IdToType<type, Ts...>::type;
	// 	new (data) T(std::move(other.get<type>()));
	// }

	template <class T>
	constexpr Variant(T&& t)
		requires (
			!std::is_same_v<std::decay_t<T>, Variant> &&
			!T_is_instantiation_of<in_place_type_t, T>::value &&
			!V_is_instantiation_of<in_place_index_t, T>::value &&
			(TypeToId<std::decay_t<T>, Ts...>::value != variant_npos || ConvertTypeToId<std::decay_t<T>, Ts...>::value != variant_npos)
		) // 可以再限制一下类型 T 在 Ts 的出现次数
	{
		using DT = std::decay_t<T>;
		constexpr size_t id = TypeToId<DT, Ts...>::value != variant_npos ? TypeToId<DT, Ts...>::value : ConvertTypeToId<DT, Ts...>::value;
		using RT = IdToType<id, Ts...>::type;
		static_assert(std::is_constructible_v<RT, T>);

		type = id;
		new (data) RT(std::forward<T>(t));
	}

	template <class T>
	Variant& operator =(T&& t)
		requires (
			!std::is_same_v<std::decay_t<T>, Variant> &&
			(TypeToId<std::decay_t<T>, Ts...>::value != variant_npos || ConvertTypeToId<std::decay_t<T>, Ts...>::value != variant_npos)
		)
	{
		using DT = std::decay_t<T>;
		constexpr size_t id = TypeToId<DT, Ts...>::value != variant_npos ? TypeToId<DT, Ts...>::value : ConvertTypeToId<DT, Ts...>::value;
		using RT = IdToType<id, Ts...>::type;
		static_assert(std::is_assignable_v<RT&, T>);

		Destroy();
		type = id;
		new (data) RT(std::forward<T>(t));
		return *this;
	}

	template <class T, class... Args>
	constexpr explicit Variant(in_place_type_t<T>, Args&&... args)
		requires (
			std::is_constructible_v<T, Args...> &&
			TypeToId<T, Ts...>::value != variant_npos &&
			CountType<T, Ts...>::value == 1
		)
	{
		type = TypeToId<T, Ts...>::value;
		new (data) T(std::forward<Args>(args)...);
	}

	template <size_t I, class... Args>
	constexpr explicit Variant(in_place_index_t<I>, Args&&... args)
		requires (I < sizeof...(Ts)) && std::is_constructible_v<typename IdToType<I, Ts...>::type, Args...>
	{
		type = I;
		using T = IdToType<I, Ts...>::type;
		new (data) T(std::forward<Args>(args)...);
	}

	// emplace
	template <class T, class... Args>
	T& emplace(Args&&... args)
		requires (
			std::is_constructible_v<T, Args...> &&
			CountType<T, Ts...>::value == 1
		)
	{
		Destroy();
		type = TypeToId<T, Ts...>::value;
		return *(new (data) T(std::forward<Args>(args)...));
	}

	template <std::size_t I, class... Args>
	variant_alternative_t<I, Variant>& emplace(Args&&... args)
		requires (I < sizeof...(Ts)) && std::is_constructible_v<typename IdToType<I, Ts...>::type, Args...>
	{
		Destroy();
		type = I;
		using T = IdToType<I, Ts...>::type;
		return *(new (data) T(std::forward<Args>(args)...));
	}

	void Destroy() {
		if (type != variant_npos) {
			destroyFuncs[type](data);
			type = variant_npos;
		}
	}

	// get
	template <typename T>
	T& get() {
		static_assert(CountType<T, Ts...>::value == 1);
		constexpr size_t index = TypeToId<T, Ts...>::value;
		return get<index>();
	}

	template <typename T>
	const T& get() const {
		static_assert(CountType<T, Ts...>::value == 1);
		constexpr size_t index = TypeToId<T, Ts...>::value;
		return get<index>();
	}

	template <size_t index>
	typename IdToType<index, Ts...>::type& get() {
		if (index != type) {
			throw std::bad_variant_access();
		}
		return *std::launder(reinterpret_cast<typename IdToType<index, Ts...>::type*>(data));
	}

	template <size_t index>
	const typename IdToType<index, Ts...>::type& get() const {
		if (index != type) {
			throw std::bad_variant_access();
		}
		return *std::launder(reinterpret_cast<typename IdToType<index, Ts...>::type*>(
			const_cast<unsigned char*>(data) // const this 只能获取到 const uchar*
		));
	}

	constexpr std::size_t index() const noexcept {
		return type;
	}

	// visit
	// 包装一下,以便将 index_seq 传入到形参包 ids
	// 注意要继续 forward 传递函数类型,不然 F 可能与传入的 func 不匹配
	template <typename F>
	auto visit(F&& func) {
		return visit2<F>(std::forward<F>(func), std::make_index_sequence<sizeof...(Ts)>{});
	}

    // 接收一个可调用对象,根据当前 type 调用其对应重载
	template <typename F, size_t... ids>
	auto visit2(F&& func, std::integer_sequence<size_t, ids...>) {
		// 使用 declval 创建 F 实例并获取其 operator (...) 方法的返回值类型
		// F 需要能接收所有选项,并返回相同类型。所以可用 T0 获取返回值类型(同样用 declval 构造 T0 对象)
		using Ret = decltype(std::declval<F>()(std::declval<T0>()));
		using VisitorT = Ret (*)(F&&, Variant&);

		// 生成下标传给各函数,以便使用下标 get(类型可以重复,因此使用 Ts... 进行类型 get 可能错误)
		// VisitorT 要与 visitImpl 完全匹配(实例化了就没有万能引用了)
		static constexpr VisitorT visitorFuncs[] = { visitImpl<F, Variant, ids>... };

		return visitorFuncs[type](std::forward<F>(func), *this);
	}

private:
	size_t type;
	// alignas(MaxAlign<Ts...>::value)
	alignas(Ts...) unsigned char data[MaxSize<Ts...>::value]; // alignas(T...) 也可展开。对象会取最大的对齐值

	using DestroyFuncT = void (*)(unsigned char*);
	static constexpr DestroyFuncT destroyFuncs[] = { DestroyImpl<Ts>... };
	// 注意不要与类内的 Destroy 定义重名,会优先选择后者
};

// template <typename... Ts>
// const typename Variant<Ts...>::DestroyFuncT Variant<Ts...>::destroyFuncs[] = { DestroyImpl<Ts>... };

// --- 非成员函数
// --- get:通过下标/类型访问 variant 的当前值。
template <size_t I, class... Ts>
variant_alternative_t<I, Variant<Ts...>>& get(Variant<Ts...>& v) {
	return v.template get<I>();
}

template <size_t I, class... Ts>
const variant_alternative_t<I, Variant<Ts...>>& get(const Variant<Ts...>& v) {
	return v.template get<I>();
}

template <class T, class... Ts>
T& get(Variant<Ts...>& v) {
	return v.template get<T>();
}

template <class T, class... Ts>
const T& get(const Variant<Ts...>& v) {
	return v.template get<T>();
}

// --- get_if:返回 variant 当前值的指针。若下标/类型与当前值不符,或 pv 是空指针,则返回 nullptr 不抛出异常。
template <size_t I, class... Ts>
constexpr std::add_pointer_t<variant_alternative_t<I, Variant<Ts...>>>
get_if(Variant<Ts...>* pv) noexcept {
	if (I != pv->index()) {
		return nullptr;
	}
	return std::addressof(pv->template get<I>());
}

template <size_t I, class... Ts>
constexpr std::add_pointer_t<const variant_alternative_t<I, Variant<Ts...>>>
get_if(const Variant<Ts...>* pv) noexcept {
	if (I != pv->index()) {
		return nullptr;
	}
	return std::addressof(pv->template get<I>());
}

template <typename T, class... Ts>
constexpr T* get_if(Variant<Ts...>* pv) noexcept {
	constexpr size_t index = TypeToId<T, Ts...>::value;
	if (index != pv->index()) {
		return nullptr;
	}
	return std::addressof(pv->template get<T>());
}

template <typename T, class... Ts>
constexpr const T* get_if(const Variant<Ts...>* pv) noexcept {
	constexpr size_t index = TypeToId<T, Ts...>::value;
	if (index != pv->index()) {
		return nullptr;
	}
	return std::addressof(pv->template get<T>());
}


// ---
struct A {
	double a, b;
	A(): a(0), b(0) {}
	A(int i): a(i), b(i) {}
	bool operator ==(const A& x) const {
		return a == x.a && b == x.b;
	}
};
struct B {
	char c;
	B() {c=0;}
	B(char c): c(c) {}
	~B() {}
	B(const B& b) {c=b.c;}
	B(B&& b) {c=b.c; b.c=0;}
};

template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

int add(int a, int b) {
	return a + b;
}
double minus(double a) {
	return -a;
}

int main() {
	using std::vector;
	using std::string;

	using std::get;
	using std::get_if;
	using std::holds_alternative;
	using std::visit;

#if false
	#define Variant std::variant
	#define in_place_type std::in_place_type
	#define in_place_index std::in_place_index
#endif

	A a{};
	B b{'0'};
	{
		Variant<A, char> v;
		assert((size_t)std::addressof(get<0>(v)) % 8 == 0);
		assert(alignof(get<0>(v)) == 8 && alignof(v) == 8);

		using V = Variant<A, int, B, string>;
		V v1;
		assert(holds_alternative<A>(v1) && v1.index() == 0 && get<A>(v1) == A{});
		assert(!holds_alternative<int>(v1));

		V v2{b};
		assert(v2.index() == 2 && get<2>(v2).c == '0' && b.c == '0');

		V v3{std::move(b)};
		assert(v3.index() == 2 && get<2>(v3).c == '0' && b.c == 0);

		V v4{"abc"};
		assert(v4.index() == 3 && get<3>(v4) == "abc");

		using V2 = Variant<A, vector<int>, string, char>;
		V2 a {in_place_type<vector<int>>, 3, (int)'a'};
		assert(a.index() == 1 && get<1>(a).size() == 3 && get<1>(a)[0] == 'a');

		V2 b {in_place_type<string>, 3, 'a'};
		assert(b.index() == 2 && get<2>(b) == "aaa");

		V2 c {in_place_index<1>, 3, 'a'};
		assert(c.index() == 1 && get<1>(c).size() == 3 && get<1>(c)[0] == 'a' && holds_alternative<vector<int>>(c));

		V2 d {in_place_index<2>, "ABCDE", 4};
		assert(d.index() == 2 && get<2>(d) == "ABCD" && holds_alternative<string>(d));
	}
	{
		Variant<int(*)(int, int), int, double(*)(double)> x = add;
		assert(get<0>(x)(1, 2) == 3);
		x.emplace<2>(minus);
		assert(get<2>(x)(3) == -3);
		x = add;
		assert(get<0>(x)(2, 3) == 5);

		Variant<string, bool, const char*> a = "abc";
		assert(a.index() == 2);

		// 不存放 const char[4]。"abc" 实际是 const char&[4],也需要 remove_ref
		Variant<string, const char*, const char[4]> b = "abc";
		assert(b.index() == 1);

		// Variant<A, in_place_index_t<1>> e;
		// e = in_place_index_t<1>{}; // error
	}
	// get
	{
		Variant<int, float> v;
		int& i = get<int>(v);
		assert(i == 0);
		i = 1;
		int j = get<0>(v);
		assert(j == 1);

		// get<3>(v); // 越界会有编译期模板错误
		// get_if<double>(&v); // 使用不存在的类型会有编译期模板错误
		try { // 抛异常
			get<float>(v);
			get<1>(v);
		}
		catch (const std::bad_variant_access&) {
			puts("catch");
		}

		assert(*get_if<0>(&v) == 1);
		assert(*get_if<int>(&v) == 1);
		assert(get_if<1>(&v) == nullptr);
		assert(get_if<float>(&v) == nullptr);
	}
	// operator =
	{
		Variant<string, string> v1;
		// v1 = string("abc"); // 实现可以这样赋值第一个 string,std 不允许,属于通过类型赋值
		get<0>(v1) = "abc";
		assert(v1.index() == 0 && get<0>(v1) == "abc");
		v1.emplace<1>("def");
		assert(v1.index() == 1 && get<1>(v1) == "def");

		using V = Variant<A, int, vector<int>, string>;
		V v2 {vector<int>{1, 2, 3}};
		assert(v2.index() == 2 && !holds_alternative<string>(v2));
		v2 = "abc";
		assert(v2.index() == 3 && get<string>(v2) == "abc");
		v2 = 3;
		assert(v2.index() == 1 && get<int>(v2) == 3);

		V v3 {1};
		assert(v3.index() == 1);
		v3 = vector<int>(2); // resize(2)
		get<2>(v3).push_back(1);
		get<2>(v3).push_back(2);
		auto& vec = get<2>(v3);
		assert(vec[0] == 0 && vec[2] == 1 && vec[3] == 2);
	}
	// emplace
	{
		Variant<string, string> v1;
		v1.emplace<0>("abc");
		assert(v1.index() == 0 && get<0>(v1) == "abc");
		v1.emplace<1>("def");
		assert(v1.index() == 1 && get<1>(v1) == "def");

		using V = Variant<A, int, vector<int>, string>;
		V v2 {vector<int>{1, 2, 3}};
		assert(v2.index() == 2 && !holds_alternative<string>(v2));
		v2.emplace<string>("abc");
		assert(v2.index() == 3 && get<string>(v2) == "abc");
		v2.emplace<0>(3);
		assert(v2.index() == 0 && get<A>(v2) == A{3});

		V v3 {1};
		assert(v3.index() == 1);
		v3.emplace<2>(2); // resize(2)
		get<2>(v3).push_back(1);
		get<2>(v3).push_back(2);
		auto& vec = get<2>(v3);
		assert(vec[0] == 0 && vec[2] == 1 && vec[3] == 2);
	}
	// else
	{
		Variant<int, long, string> v = 3l;
		if (auto p = get_if<long>(&v)) {
			assert(*p == 3l);
		} else {
			assert(false);
		}
		v = 5;
		if (holds_alternative<long>(v)) {
			assert(false);
		} else if (holds_alternative<int>(v)) {
			assert(get<int>(v) == 5);
		}
	}
	// visit
	{
		Variant<int, string> v = 9;

		auto f = [](auto&& arg) {
			cout << "v(9): " << arg << '\n';
		};
		visit(f, v);

		v = "abc";
		visit([](auto&& arg) {
			using T = std::decay_t<decltype(arg)>;
			if constexpr (std::is_same_v<T, int>)
				assert(false);
			else if constexpr (std::is_same_v<T, string>)
				assert(arg == "abc");
			else
				static_assert(false, "error type");
		}, v);

		auto val = visit(overloaded{
			[](int arg) { assert(arg == 1); return 1; },
			[](const string& arg) { assert(arg == "abc"); return 2;},
		}, v);
		assert(val == 2);
	}
	// TODO
	{
		std::variant<float, long, double> b = 0;
		assert(b.index() == 1); // 目前实现的结果将是 0,float/double/char 都不应匹配 int,要换一种匹配方式
	}

	// type check
	using i2t1 = IdToType<0, double, int, float, char[3]>::type;
	using i2t2 = IdToType<2, double, int, float, char[3]>::type;
	using i2t3 = IdToType<3, double, int, float, char[3]>::type;
	// using f = FirstType<int, double, A[2]>::type;

	[](...) {}(
		MaxSize<int, char, double, char[3], A[2]>::value,
		MaxAlign<int, char, A, char[3], A[2]>::value,

		TypeToId<int, int, char[3], double, A>::value,
		TypeToId<char[3], int, char[3], double, A>::value,
		TypeToId<A[2], int, char[3], double, A[2]>::value,
		TypeToId<A[2], int, char[3], double, A>::value,

		TypeToId<const char*, std::string>::value,
		ConvertTypeToId<const char*, int, std::string>::value,
		ConvertTypeToId<const char*, std::string, std::string>::value,

		// std::is_same_v<const char*, const char[4]>, // false
		TypeToId<const char[4], std::string, const char*,  const char[4]>::value, // const char[4] 无意义,里面不需要放数组。此时直接匹配 const char*
		TypeToId<const char*, std::string, const char*>::value,
		ConvertTypeToId<const char[4], std::string, const char*>::value, // 实际应该是 1,判断时需用 decay(T)

		TypeToId<decltype(0), float, long, double>::value,
		ConvertTypeToId<decltype(0), float, long, double>::value, // 将匹配第一个 float,实际应该是 long

		T_is_instantiation_of<in_place_type_t, in_place_type_t<int>>::value,
		T_is_instantiation_of<in_place_type_t, in_place_index_t<1>>::value,
		V_is_instantiation_of<in_place_index_t, in_place_index_t<1>>::value

		// CheckAll<std::is_copy_constructible, A, int, B>::value
	);

}

std::any

#include <any>
#include <cstdio>
#include <vector>
#include <iostream>
#define pc putchar

using std::cout;
using std::string;
using std::vector;

template<typename T>
std::ostream& operator << (std::ostream& out, const vector<T>& v) {
	out << "[";
	for (auto it = v.begin(); it != v.end(); it++) {
		if (it != v.begin()) out << ", ";
		out << *it;
	}
	out << "]";
	return out;
}

class Any {
public:
	Any(): data(nullptr) {}
	Any(const Any& other) {
		if (other.data) {
			data = other.data->clone();
		}
	}
	Any(Any&& other): data{other.data} {
		other.data = nullptr;
	}
	Any& operator =(const Any& other) {
		if (this == &other) {
			return *this;
		}
		reset();
		if (other.data) {
			data = other.data->clone();
		}
		return *this;
	}
	Any& operator =(Any&& other) {
		reset();
		data = other.data;
		other.data = nullptr;
		return *this;
	}

	template <typename T,
			typename = std::enable_if_t<!std::is_same_v<Any, std::decay_t<T>>>>
	Any(T&& v): data(new Data<std::decay_t<T>>(std::forward<T>(v))) {}
	// 注意 Data 的模板上不能带引用,不然就不能用Data<T>准确获取了。但为了完美转发,构造函数上的T还是要带引用
	// 此外 Data 的成员 data 的类型也不能带引用,否则传左值时它就是 T&,会获取引用,而不是进行拷贝,可能导致悬垂引用!
	// 但与 remove_reference 相比,decay 更合适,见 function

	~Any() {
		reset();
	}

	template <typename T>
	T& any_cast() {
		auto p = data == nullptr ? nullptr : dynamic_cast<Data<T>*>(data);
		if (p == nullptr) {
			throw std::bad_any_cast();
		}
		return p->data;
		// return ((Data<T>*)data)->data;
	}

	void reset() noexcept {
		delete data;
	}

	void swap(Any& other) noexcept {
		std::swap(data, other.data);
	}

	const std::type_info& type() const noexcept {
		return data ? data->type() : typeid(nullptr);
	}

	friend std::ostream& operator << (std::ostream& out, const Any& any);

private:
	struct Base {
		virtual ~Base() {}
		virtual Base* clone() const = 0;
		virtual const std::type_info& type() const noexcept = 0;
		virtual std::ostream& print(std::ostream& out) = 0;
	};

	Base* data;

	template <typename T>
	struct Data: Base {
		template <typename UT>
		Data(UT&& v): data(std::forward<UT>(v)) {
			static_assert(std::is_same_v<T, std::decay_t<UT>>);
		}
		// 为了实现完美转发,构造函数中的模板必须带引用;但类本身的模板不能带引用,所以只能写两个模板参数

		Base* clone() const override {
			return new Data<T>(*this);
		}
		const std::type_info& type() const noexcept override {
			return typeid(T);
		}
		std::ostream& print(std::ostream& out) override { // 类型需要重载 operator <<
			out << data;
			return out;
		}

		T data;
	};
};

std::ostream& operator << (std::ostream& out, const Any& any) {
	if (any.data) {
		any.data->print(out);
	} else {
		out << "null";
	}
	return out;
}

struct Test {
	Test() { x = 0; debug && puts("Test()");}
	~Test() { debug && printf("~Test(%d)\n", x);}
	Test(const Test& o) { x = o.x; debug && puts("Test(const Test&)");}
	Test(Test&& o) { x = o.x; o.x = 0; debug && puts("Test(&&)");}

	int x;
	static constexpr bool debug = false;
};

int main() {
	if (true) {
		vector<Any> vec{1, 2.0, 3.f, "abc", vector<Any>{'a', "str"}};
		cout << vec << '\n';
		vec[2] = vector<int>{3, 3};
		cout << vec << '\n';
	}
	if (false) {
		Any a = Test();
		cout << a.any_cast<Test>().x << '\n'; // 0
	}
	if (false) {
		Test t;
		t.x = 1;
		Any b = t;
		cout << b.any_cast<Test>().x << '\n'; // 1
		t.x = 2;
		cout << b.any_cast<Test>().x << '\n'; // 1
		b.any_cast<Test>().x *= -1;
		Test t2 = b.any_cast<Test>();
		cout << t2.x << '\n'; // -1
		// copy
		Any c = b;
		Test& t3 = c.any_cast<Test>();
		cout << t3.x << '\n'; // -1
		t3.x --;
		cout << c.any_cast<Test>().x << '\n'; // -2
		Any d = std::move(c);
		try {
			cout << d.any_cast<Test>().x << '\n'; // -2
			cout << c.any_cast<Test>().x << '\n'; // error
		}
		catch(const std::exception& e) {
			std::cerr << e.what() << '\n';
		}
	}
	if (true) {
		Any a = 5;
		Any b = 1.f;
		Any c = std::vector<int>{1, 2};
		cout << a.type().name() << '\n';
		cout << b.type().name() << '\n';
		cout << c.type().name() << '\n';
	}

	cout << "end\n";

}

std::function

#include <cstdio>
#include <iostream>
#include <functional>
using std::cout;

// class bad_function_call: public std::exception {
// 	virtual const char* what() const noexcept {
// 		return "bad function call";
// 	}
// };

template <typename>
class Function;

template <typename R, typename... Args>
class Function<R(Args...)> {
public:
	Function(): callable(nullptr) {}

	Function(const Function& other) {
		if (other.callable) {
			callable = other.callable->clone();
		}
	}

	Function(Function&& other): callable(other.callable) { // 注意这里不要clone,直接获取值
		other.callable = nullptr;
	}

	template <typename F,
			typename = std::enable_if_t<!std::is_same_v<Function, std::decay_t<F>>>>
	Function(F&& f): callable(new NormalCallable<std::decay_t<F>>(std::forward<F>(f))) {}
	// 模板类型不能带引用,否则会产生悬垂引用,见 any
	// 但去除引用后,NormalCallable 的 F 会被推导为原类型,赋值函数时将是函数类型
	// 而数据成员的类型不能是函数类型。使用 decay 而非 remove_reference 刚好可做到这点(对于函数类型,会转为函数指针)
	// 但是 decay 后 Function g = f; 会匹配到这个模板构造函数,而非 Function(const Function&),除非加 enable_if 或 Function(Function&)

	template <typename C, typename Func>
	Function(Func C::* f): callable(new MemberFunctionPointer<C, Func>(std::forward<Func C::*>(f))) {}

	~Function() {
		delete callable;
	}

	operator bool() const noexcept {
		return this->callable != nullptr;
	}

	R operator()(Args... args) {
		if (static_cast<bool>(*this) == false) {
			throw std::bad_function_call();
		}
		return callable->invoke(std::forward<Args>(args)...);
	}

	void swap(Function &other) noexcept {
		std::swap(callable, other.callable);
	}

private:
	// 抽象基类,用于存储可调用对象的指针
	class BaseCallable {
	public:
		virtual R invoke(Args... args) = 0;
		virtual ~BaseCallable() {}
		virtual BaseCallable* clone() const = 0;
	};

	BaseCallable* callable;

	// 具体实现类,用于存储特定类型的可调用对象
	// #1 存储普通函数指针、仿函数、lambda、bind
	template <typename F>
	class NormalCallable : public BaseCallable {
	public:
		template <typename T>
		NormalCallable(T&& f) : func(std::forward<T>(f)) {
			// 对于仿函数,F为仿函数类型,T为 F&& 或 F&
			// 对于普通函数,F为函数指针,T为函数类型(可能带引用)
			static_assert(std::is_same_v<F, std::decay_t<T>>);
		}

		R invoke(Args... args) override {
			return func(std::forward<Args>(args)...);
		}

		BaseCallable* clone() const override {
			return new NormalCallable(*this);
		}

	private:
		F func;
	};

	// #2 存储函数成员指针
	template <typename C, typename F>
	class MemberFunctionPointer : public BaseCallable {
	public:
		MemberFunctionPointer(F C::*f) : func(f) {}

		R invoke(Args... args) override {
			return invokeImpl(args...);
		}
		template <typename... Args2>
		R invokeImpl(C& object, Args2... args) {
			return (object.*func)(std::forward<Args2>(args)...);
		}
		template <typename... Args2>
		R invokeImpl(const C& object, Args2... args) {
			return (object.*func)(std::forward<Args2>(args)...);
		}

		BaseCallable* clone() const override {
			return new MemberFunctionPointer(*this);
		}

	private:
		F C::*func;
	};
};

struct Node {
	Node(int x): x(x) {}
	int f(int v) {
		cout << "Node::f(" << v << ") x:" << x << '\n';
		return x;
	}
	int const_f(int v) const {
		cout << "Node::const_f(" << v << ") x:" << x << '\n';
		return x;
	}
	int x;
};
struct Func {
	int operator()(int x) {
		return x + 1;
	}
};

int add(int x) {
	return x + 1;
}
int add2(int x, int y) {
	return x + y;
}

int main() {
	// #define Function std::function
	// #1 存储普通函数指针
	Function<int (int)> f_add = add;
	cout << f_add(1) << '\n'; // 2
	Function<int (int)> f_add_c = f_add;
	cout << f_add_c(1) << '\n'; // 2

	// 通过 clone 创建新的 callable
	auto f_add2 = std::move(f_add);
	cout << f_add2(2) << '\n'; // 3
	// cout << f_add(2) << '\n'; // bad_function_call

	// #2 存储成员函数指针
	Node node{1};
	Function<int (Node&, int)> fp = &Node::f; // 通过Node&实例调用(也可以是const Node&)
	cout << fp(node, 2) << '\n'; // 1
	Function<int (const Node&, int)> fpc = &Node::const_f; // 通过const Node&实例调用
	cout << fpc(1, 2) << '\n'; // 1 隐式构造的右值可以绑定到const左值引用

	// 通过 clone 创建新的 callable
	auto fp2 = fp;
	cout << fp2(node, 3) << '\n'; // 1

	// #3 (不支持)存储数据成员指针
	// Function<int& (Node&)> fr = &Node::x; // 也可以是 int (const Node&)
	// cout << fr(node) << '\n'; // 1
	// fr(node) = 3;
	// cout << node.x << '\n'; // 3

	// #4 存储仿函数
	Function<int (int)> ff = Func();
	cout << ff(3) << '\n'; // 4

	// #5 存储lambda
	int tmp = 1;
	auto lam = [&tmp](int x) {
		tmp += x;
		return tmp;
	};
	Function<int (int)> f_lam = lam;
	cout << f_lam(2) << '\n'; // 3
	cout << tmp << '\n'; // 3

	// #6 存储bind表达式
	Function<int (int)> f_bind = std::bind(add2, 2, std::placeholders::_1);
	cout << f_bind(3) << '\n'; // 5
}

function_ref

#include <cassert>
#include <iostream>
#include <type_traits>

using std::cout;

/*
类似 string_view,可以设计不包含函数所有权、只是简单使用的轻量级 function_ref,能够赋值任何可调用对象,但比 function 高效轻巧。
它不负责函数捕获变量的生命周期,一定要在引用值的生命周期内使用。

void* 可以保存一切指针,但还需要保存、恢复函数的类型信息。
而模板参数会作为模板定义类型的一部分,以便使用,因此可以保存一个模板函数或模板类,将函数类型放在模板参数上,模板内部将 void* 指针转为模板参数(即原本函数)的类型返回或直接调用。

TODO:
无法保存成员函数指针,需要像 Function 一样对其做特化,因为不能用 f(args) 调用,需要 .*。
如果保存的只是函数指针,可以优化为 8B。
*/

template <typename>
class FunctionRef;

template <typename Ret, typename... Args>
class FunctionRef<Ret(Args...)> { // 除了无所有权外,与 function 类似
private:
	using ConverterType = Ret(*)(void*, Args...);
	void* p;
	ConverterType converter;

	template <typename Func>
	static Ret ConvertAndCall(void* p, Args... args) {
		auto f = *reinterpret_cast<Func*>(p);
		return f(std::forward<Args>(args)...);
	}

public:
	FunctionRef(): p(nullptr), converter(nullptr) {}

	template <typename Func>
	FunctionRef(Func&& f) {
		p = reinterpret_cast<void*>(&f);
		converter = ConvertAndCall<std::remove_reference_t<Func>>; // 保存实例化(包含了类型信息)的模板;注意参数 Func 是带引用的
	}

	/*
	Args 是所存函数形参的类型(最初的模板参数),而非推断出的实参类型。
	虽然没有万能引用,但这里使用 forward 也能正确转发 args:
	- 当 Args 是 A 时,args 是值拷贝的临时量,forward<A>(a) 等价于 move(a) 使用右值传递
	- 当 Args 是 A& 时,forward<A>(a) 折叠后依旧使用 A& 传递
	- 当 Args 是 const A& 时,forward<A>(a) 依旧使用 const A& 传递
	*/
	Ret operator ()(Args... args) {
		return converter(p, std::forward<Args>(args)...);
	}

	operator bool() {
		return p != nullptr;
	}
};

// ---
int sum(int a, int b) {
	return a + b;
}
int mul(int a, int b) {
	return a * b;
}
int foo(FunctionRef<int(int, int)> fRef, int x, int y) {
	return fRef(x, y);
}

struct A {
	int f(int x) {
		return x * 2;
	}
	int operator ()(int x) {
		return x + 1;
	}
};

int main() {
	// 普通函数
	FunctionRef<int(int, int)> f1 = sum;
	assert(f1(3, 5) == 8);
	f1 = mul;
	assert(f1(3, 5) == 15);

	// lambda
	int cnt = 1;
	auto lambda = [&](int x, int y) {cnt+=x+y; return cnt; };
	assert(foo(lambda, 1, 2) == 4 && cnt == 4);
	f1 = lambda;
	assert(f1(1, 2) == 7 && cnt == 7);

	A a;
	// 不支持成员函数
	// FunctionRef<int(A&, int)> af = &A::f;
	// assert(af(a, 3) == 6);

	// 仿函数
	FunctionRef<int(int)> af2 = a;
	assert(af2(3) == 4 && a(3) == 4);
}

std::make_integer_sequence O(logn)

#include <type_traits>

template <class T, T... Ints>
struct integer_sequence {};

// --- 常规 O(n) 实现,每次向可变参数中添加一个 N-1,最后用它构造整个 sequence
namespace impl1 {

template <class T, T N, T... Ints>
struct make_integer_sequence_impl {
	using type = make_integer_sequence_impl<T, N-1, N-1, Ints...>::type; // 添加 N-1 到 Ints
};

// 在递归到0时,make_integer_sequence_impl 的形参包拥有 0~N-1
// 如果不进行约束,需要将 N 和 Ints 设为 size_t 类型,才能写 make<T, 0> 特化(否则类型会依赖 T,不能直接用0)
template <class T, T N, T... Ints>
	requires (N == 0)
struct make_integer_sequence_impl<T, N, Ints...> {
	using type = integer_sequence<T, Ints...>;
};

template <class T, T N>
using make_integer_sequence = make_integer_sequence_impl<T, N>::type;

using seq3 = make_integer_sequence<int, 3>;
static_assert(std::is_same_v<seq3, integer_sequence<int, 0, 1, 2>>);

} // namespace impl1

// --- 常规 O(n) 实现,每次向可变参数中添加一个 N-1,最后用它构造整个 sequence
namespace impl2 {
// 继承 conditional 而非直接使用 type = conditional<..., make::type>,可以避免 conditional 因为使用到了 make 的 type 成员而实例化 make 导致无限递归

template <class T, T N, T... Ints>
struct make_integer_sequence_impl:
	std::conditional_t<
			(N == 0),
			std::type_identity<integer_sequence<T, Ints...>>,
			make_integer_sequence_impl<T, N-1, N-1, Ints...>> {}; // 添加 N-1 到 Ints

template <class T, T N>
using make_integer_sequence = make_integer_sequence_impl<T, N>::type;

using seq3 = make_integer_sequence<int, 3>;
static_assert(std::is_same_v<seq3, integer_sequence<int, 0, 1, 2>>);

} // namespace impl2

// --- O(log n) 实现。分治,每次将 make(N) 变成两个 make(N/2),生成两个 0~N/2-1 的模板(它们是同一个模板),然后利用包展开为后者的每个模板值加 N/2,合并成 make(N)
// 在递归到 1 时,make(1) 的形参包拥有 0;在递归到 2 时,make(2) 为 merge(make(1)) 即 0, 1;递归到 N 时,make(N) 为 merge(make(N/2)) 即 0, ..., N/2-1, N/2, ..., N-1
namespace impl3 {

template <class T, T N, T... Ints>
struct make_integer_sequence_impl;

template <class Seq, auto val>
struct double_sequence;

template <class T, T... Ints, T val>
struct double_sequence<make_integer_sequence_impl<T, Ints...>, val> {
	using type =
		std::conditional_t<val == 0,
			make_integer_sequence_impl<T, Ints..., (Ints + sizeof...(Ints))...>,
			make_integer_sequence_impl<T, Ints..., (Ints + sizeof...(Ints))..., sizeof...(Ints) * 2>
		>; // 当 N 为奇数时,在 make 最后附加一个数
};

template <class T, T N, T... Ints>
struct make_integer_sequence_impl {
	using type = double_sequence<typename make_integer_sequence_impl<T, N/2>::type, N&1>::type;
};

template <class T, T N>
	requires (N == 1)
struct make_integer_sequence_impl<T, N> {
	using type = make_integer_sequence_impl<T, 0>;
};

// 在用一个类将 make(Ints...) 转为 integer_sequence<Ints...>。可以在 make 里定义,但这会实例化多个 integer_sequence
template <class T>
struct make_integer_sequence_converter;

template <class T, T... Ints>
struct make_integer_sequence_converter<make_integer_sequence_impl<T, Ints...>> {
	using type = integer_sequence<T, Ints...>;
};

template <class T, T N>
using make_integer_sequence = make_integer_sequence_converter<typename make_integer_sequence_impl<T, N>::type>::type;

using seq6 = make_integer_sequence<int, 6>;
using seq11 = make_integer_sequence<int, 11>;
static_assert(std::is_same_v<seq6, integer_sequence<int, 0, 1, 2, 3, 4, 5>>);
static_assert(std::is_same_v<seq11, integer_sequence<int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10>>);

} // namespace impl3


int main() {


}

状态元编程

https://zhuanlan.zhihu.com/p/646752343

STMP 未被标准支持,可能是非良构的,并且容易导致 odr violation(违反 ODR),比如:如果 A, B 都使用了 STMP 实现的计数器,C 先 include A 再 B,D 先 include B 再 A,那么编译后的 C 和 D 中的 A, B 得到的计数器值是不同的(注意 include 是文本替换,再编译)。

C++ 常规的常量表达式求值既不依赖、也不能改变全局的状态,它们类似一个纯函数,只要输入确定,输出就确定。模板元编程也是如此。
状态元编程 (stateful template meta programming) 能够在编译期的常量求值中引入全局状态,使同样的常量表达式可以求值出不同结果。
比如:实现一个编译期计数器:

constexpr int a = f();
constexpr int b = f();
constexpr int c = f();
static_assert(a == 1 && b == 2 && c == 3); // 常规的模板变成无法实现,f 将始终得到相同结果

STMP 的原理:通过控制模板实例化来控制函数何时定义;根据函数是否定义返回不同的值。

友元函数可以在类外部声明,在类内部定义;如果这个类是一个类模板,那么只有在实例化类时才会定义该函数:

auto flag();

template <bool val> // 任意模板参数
struct flag_setter {
	friend auto flag() {}
};

int main() {
	// flag();  // error,无法推导 auto
	flag_setter<true>{};
	flag();  // ok,实例化了 flag_setter 并定义了 flag
}

注意函数的返回类型需要为 auto,这样当不存在类的实例化和 flag 的定义时,调用 flag 会因为没有定义、无法确定返回值类型而先推导失败(属于 SFINAE,不是严重错误);如果不是 auto 而是固定类型,则会因为没有定义而直接导致编译失败(不是推导失败,属于错误),并且依然能找到之后实例化后的 flag。(不确定?)
推导失败不是错误,仍可继续执行;推导时只会考虑当前位置及以前的函数定义,之后再定义函数不会影响 auto 推导和找不到定义。函数未定义是错误,直接导致编译失败;检查函数定义时会检查整个翻译单元中是否有该函数。

通过这个逻辑并检查flag()是否是合法表达式,就可以实现0到1的计数器:

auto flag();

template <bool val>
struct flag_setter {
	friend auto flag() {}
};

template <bool condition = requires { flag(); }>
consteval int value() {
	if constexpr(!condition) {
		constexpr flag_setter<false> tmp;
		return 0;
	} else {
		return 1;
    }
}

int main() {
	constexpr auto a = value();
	constexpr auto b = value();
	static_assert(a == 0 && b == 1);
}

模板实例化时会进行两阶段名字查找:在第一阶段检查语法是否正确;在第二阶段(即实例化时)替换模板参数,再检查是否正确。
(某些编译器将所有检查放在第二阶段)
所以某些不依赖模板参数的错误,在实例化前就可以被找到,导致 CE。
所以上述代码可能要修改flag(),让它依赖待决名,避免直接因为 flag 未定义而报错:

auto flag(int);

template <bool val>
struct flag_setter {
	friend auto flag(int) {}
};

template <int arg = 0, bool condition = requires { flag(arg); }> // arg 可以是任意模板参数
consteval int value() { ... }

通过类模板reader<N>将友元函数 flag 扩展到 N 个(每个 reader 有一个),然后检查flag(reader<N>{})是否定义,就可以实现 0~N-1 的计数。

template <size_t N>
struct reader {
    friend auto flag_counter(reader<N>); // 声明
};

template <size_t N>
struct setter {
	friend auto flag_counter(reader<N>) {} // 定义
};

template <size_t N = 0,
			auto tag = []{},
			bool condition = requires { flag_counter(reader<N>{}); }>
consteval auto next() {
	if constexpr (!condition) {
		constexpr setter<N> tmp; // 定义 flag(reader<N>)
		return N;
	} else { // 注意这里要 else,如果没有,也会在 condition 为 false 时生成该分支代码
		return next<N + 1>(); // 可以优化成指数级增长和检查
	}
}

int main() {
	constexpr auto a = next();
	constexpr auto b = next();
	constexpr auto c = next();
	static_assert(a == 0 && b == 1 && c == 2);
}

这里 flag 是在类内声明和定义的友元函数,只能通过 ADL 找到,也不会影响命名空间。

注意函数 next 中需要一个默认参数auto tag = []{}
常量表达式是纯的,即如果函数的各实参都相同,它的结果也应当相同,所以编译器可以缓存对同一函数的调用结果,避免多次调用,不符合我们的预期。因为不同位置创建的 lambda 都是不同的类型(即使看起来相同),并且函数的默认参数会在每次执行时求值,所以使用一个 lambda 作为默认模板实参,可以在每次调用函数时产生不同的模板实参、不同的函数和常量表达式,从而避免编译器缓存结果。
使用source_location::line作为默认实参,也可以实现同样的效果。

STMP 实现 Any

https://zhuanlan.zhihu.com/p/646812253

在用 STMP 实现计数器时,是通过一个每次调用都有不同模板实参的模板函数 next,而不同 next 的返回值也不需要相同,可以用这个函数做一个类型列表:只需要将原本的实例化 f(reader<N>) 映射到计数 N更改为实例化 f(reader<N>) 映射到类型 TN,就可以创建一个可自行添加的类型列表,并可通过下标查询;通过在 setter 上额外附近一个类型 T 记录当前的类型列表,就可以在任意类型列表的基础上添加类型(换句话说就是可以删除类型)。

通过这个类型列表可以实现自行注册和查询类型的 Any,在使用时不需要 any_cast 自行转换,可以直接通过 visit 读取。

但与普通 TMP 实现的 typelist 相比,区别是?

posted @ 2023-12-26 22:35  SovietPower  阅读(50)  评论(0编辑  收藏  举报