2411C++,C++26反射示例

参考

namespace __impl {template<auto... vals>struct replicator_type {template<typename F>constexpr void operator>>(F body) const {(body.template operator()<vals>(), ...);}};template<auto... vals>replicator_type<vals...> replicator = {};
}
template<typename R>
consteval auto expand(R range) {std::vector<std::meta::info> args;for (auto r : range) {args.push_back(reflect_value(r));}return substitute(^__impl::replicator, args);
}

用法:

 //用`扩展`语句
template <typename E>requires std::is_enum_v<E>
constexpr std::string enum_to_string(E value) {template for (constexpr auto e : std::meta::enumerators_of(^E)) {if (value == [:e:]) {return std::string(std::meta::identifier_of(e));}}return "<unnamed>";
}//使用`扩展`解决方法
template<typename E>requires std::is_enum_v<E>
constexpr std::string enum_to_string(E value) {std::string result = "<unnamed>";[:expand(std::meta::enumerators_of(^E)):] >> [&]<auto e>{if (value == [:e:]) {result = std::meta::identifier_of(e);}};return result;
}

示例

3.1来回

第一例并不引人注目,而是要展示如何在反射域和语法域来回切换:

constexpr auto r = ^int;
typename[:r:] x = 42;       //等价于:`int x=42;`
typename[:^char:] c = '*';  //等价于:`charc='*';`

在与依赖全名相同环境中,即,在标准叫做仅类型的环境中,可省略型名前缀.如:

using MyType = [:sizeof(int)<sizeof(long)? ^long : ^int:];  //隐式`"型名"`前缀.

3.2选择成员

第二个示例允许为特定类型"按序号"选择成员:

struct S { unsigned i:2, j:6; };
consteval auto member_number(int n) {if (n == 0) return ^S::i;else if (n == 1) return ^S::j;
}
int main() {S s{0, 0};s.[:member_number(1):] = 42;  //等价于:`s.j=42;`s.[:member_number(5):] = 0;   //错误`(member_number(5))`不是一个常数.
}

此例还说明了可以访问字段.

注意,像s.[:member_number(1):]此"访问成员拼接"是比传统语法更直接的访问成员机制.它不涉及查找成员名,检查访问,或如果拼接反射值表示成员函数解析重载.

该提案包括许多常值"元函数",可用它们内省各种语言结构.这些元函数包括,描述给定类型非静态成员返回一个反射值向量std::meta::nonstatic_data_members_of.
因此,可重写上例为:

struct S { unsigned i:2, j:6; };
consteval auto member_number(int n) {return std::meta::nonstatic_data_members_of(^S)[n];
}
int main() {S s{0, 0};s.[:member_number(1):] = 42;  //等价于:`s.j=42;`s.[:member_number(5):] = 0;   //错误`(member_number(5))`不是一个常数.
}

此提案指定std::meta名字空间与(std::meta::info)反射类型关联;因此,在上例中可省略std::meta::限定.

另一个经常有用元函数是返回一个,描述声明给定反射值表示的实例的std::string_view标识的std::meta::identifier_of.

有了此工具,可按"串"访问非静态数据成员:

struct S { unsigned i:2, j:6; };
consteval auto member_named(std::string_view name) {for (std::meta::info field : nonstatic_data_members_of(^S)) {if (has_identifier(field) && identifier_of(field) == name)return field;}
}
int main() {S s{0, 0};s.[:member_named("j"):] = 42;  //等价于:`s.j=42;`s.[:member_named("x"):] = 0;   //错误`(member_named("x")`不是一个常数.
}

3.3类型列表到大小列表

在此,大小是一个用{sizeof(int),sizeof(float),sizeof(double)}初化的std::array<std::size_t,3>:

constexpr std::array types = {^int, ^float, ^double};
constexpr std::array sizes = []{std::array<std::size_t, types.size()> r;std::views::transform(types, r.begin(), std::meta::size_of);return r;
}();

比较此方法与以下基于类型生成相同数组大小的方法:

template<class...> struct list {};
using types = list<int, float, double>;
constexpr auto sizes = []<template<class...> class L, class... T>(L<T...>) {return std::array<std::size_t, sizeof...(T)>{{ sizeof(T)... }};
}(types{});

3.4实现make_integer_sequence

与使用平凡模板元编程手动方法相比,尽管今天的标准库依赖内部函数,可提供更好make_integer_sequence实现:

#include <utility>
#include <vector>
template<typename T>
consteval std::meta::info make_integer_seq_refl(T N) {std::vector args{^T};for (T k = 0; k < N; ++k) {args.push_back(std::meta::reflect_value(k));}return substitute(^std::integer_sequence, args);
}
template<typename T, T N>using make_integer_sequence = [:make_integer_seq_refl<T>(N):];

注意,替换模板过程中隐式缓存仍适用.因此,多次使用make_integer_sequence<int,20>计算只涉及一次make_integer_seq_refl<int>(20).

3.5取类布局

struct member_descriptor
{std::size_t offset;std::size_t size;
};
//返回`std::array<member_descriptor,N>`
template <typename S>
consteval auto get_layout() {constexpr auto members = nonstatic_data_members_of(^S);std::array<member_descriptor, members.size()> layout;for (int i = 0; i < members.size(); ++i) {layout[i] = {.offset=offset_of(members[i]).bytes, .size=size_of(members[i])};}return layout;
}
struct X
{char a;int b;double c;
};
/*`常式`*/ auto Xd = get_layout<X>();/*其中`Xd`将是`std::array<member_descriptor,3>{{{0,1},{4,4},{8,8}}}`*/

3.6枚举转串

最常见工具之一按串转换枚举值,此例依赖扩展语句:

template <typename E>requires std::is_enum_v<E>
constexpr std::string enum_to_string(E value) {template for (constexpr auto e : std::meta::enumerators_of(^E)) {if (value == [:e:]) {return std::string(std::meta::identifier_of(e));}}return "<unnamed>";
}
enum Color { red, green, blue };
static_assert(enum_to_string(Color::red) == "red");
static_assert(enum_to_string(Color(42)) == "<unnamed>");

也可反向:

template <typename E>requires std::is_enum_v<E>
constexpr std::optional<E> string_to_enum(std::string_view name) {template for (constexpr auto e : std::meta::enumerators_of(^E)) {if (name == std::meta::identifier_of(e)) {return [:e:];}}return std::nullopt;
}

但是不必使用扩展语句,也可用算法.如,enum_to_string也可这样实现,此例依赖非瞬态常式分配,这也演示了根据枚举器个数选择不同算法:

template <typename E>requires std::is_enum_v<E>
constexpr std::string enum_to_string(E value) {constexpr auto get_pairs = []{return std::meta::enumerators_of(^E)| std::views::transform([](std::meta::info e){return std::pair<E, std::string>(std::meta::extract<E>(e), std::meta::identifier_of(e));})};constexpr auto get_name = [](E value) -> std::optional<std::string> {if constexpr (enumerators_of(^E).size() <= 7) {//如果枚举器不多,请使用`find_if()`的向量constexpr auto enumerators = get_pairs() | std::ranges::to<std::vector>();auto it = std::ranges::find_if(enumerators, [value](auto const& pr){return pr.first == value;};if (it == enumerators.end()) {return std::nullopt;} else {return it->second;}} else {//如果有很多枚举器,请使用`find()`的`映射`constexpr auto enumerators = get_pairs() | std::ranges::to<std::map>();auto it = enumerators.find(value);if (it == enumerators.end()) {return std::nullopt;} else {return it->second;}}};return get_name(value).value_or("<unnamed>");
}

在编译时,可根据enumerators_of长度选择更复杂查找算法(^E)

可生成紧凑双向持久数据结构,以最小的消费空间同时支持enum_to_stringstring_to_enum等.

3.7解析命令行选项

下一例展示了命令行选项解析器,如何根据成员名自动推导标志来工作.真正的命令行解析器当然会更复杂,这仅是个开始.

template<typename Opts>
auto parse_options(std::span<std::string_view const> args) -> Opts {Opts opts;template for (constexpr auto dm : nonstatic_data_members_of(^Opts)) {auto it = std::ranges::find_if(args,[](std::string_view arg){return arg.starts_with("--") && arg.substr(2) == identifier_of(dm);});if (it == args.end()) {//未提供选项,请使用`默认`continue;} else if (it + 1 == args.end()) {std::print(stderr, "Option {} is missing a value\n", *it);std::exit(EXIT_FAILURE);}using T = typename[:type_of(dm):];auto iss = std::ispanstream(it[1]);if (iss >> opts.[:dm:]; !iss) {std::print(stderr, "Failed to parse option {} into a {}\n", *it, display_string_of(^T));std::exit(EXIT_FAILURE);}}return opts;
}
struct MyOpts {std::string file_name = "input.txt";  //`"-file_name<string>"`选项int    count = 1;                     //`"-count<int>"`选项
};
int main(int argc, char *argv[]) {MyOpts opts = parse_options<MyOpts>(std::vector<std::string_view>(argv+1, argv+argc));
}

3.8简单元组类型

#include <meta>
template<typename... Ts> struct Tuple {struct storage;static_assert(is_type(define_class(^storage, {data_member_spec(^Ts)...})));storage data;Tuple(): data{} {}Tuple(Ts const& ...vs): data{ vs... } {}
};
template<typename... Ts>struct std::tuple_size<Tuple<Ts...>>: public integral_constant<size_t, sizeof...(Ts)> {};
template<std::size_t I, typename... Ts>struct std::tuple_element<I, Tuple<Ts...>> {static constexpr std::array types = {^Ts...};using type = [: types[I] :];};
consteval std::meta::info get_nth_field(std::meta::info r, std::size_t n) {return nonstatic_data_members_of(r)[n];
}
template<std::size_t I, typename... Ts>constexpr auto get(Tuple<Ts...> &t) noexcept -> std::tuple_element_t<I, Tuple<Ts...>>& {return t.data.[:get_nth_field(^decltype(t.data), I):];}
//其他值类一样...

此例使用"神奇"的std::meta::define_class模板及nonstatic_data_members_of,元函数成员反射实现类似std::tuple类型,无需在这些工具不可用时,涉及一般复杂且贵模板元编程技巧.

define_class反射不完整的类或联,加上非静态数据成员描述的向量,并完成给定类或联类型以取得所描述的成员.

3.9简单变量类型

类似如何对每个带一个成员Ts...,使用define_class实现一个元组动态创建一个类型,可实现一个只定义一个而不是结构的变量.

这里的区别当前如何定义联的析构器:

union U1 {int i;char c;
};
union U2 {int i;std::string s;
};

U1有个平凡析构器,但按已删除(因为std::string有个非平凡析构器)定义U2析构器.
但是,为了define_class,这里实际上只有一个合理的待选选项:

template <class... Ts>
union U {//所有成员Ts... members;//如果所有类型都是简单的可析构,则默认析构器constexpr ~U() requires (std::is_trivially_destructible_v<Ts> && ...) = default;//...否则,析构器闲着constexpr ~U() { }
};

如果让define_class该行为,则就可用比当前实现更直接的方式,实现一个变量.这不是std::variant完整实现,但可说明该想法:

template <typename... Ts>
class Variant {union Storage;struct Empty { };static_assert(is_type(define_class(^Storage, {data_member_spec(^Empty, {.name="empty"}),data_member_spec(^Ts)...})));static consteval std::meta::info get_nth_field(std::size_t n) {return nonstatic_data_members_of(^Storage)[n+1];}Storage storage_;int index_ = -1;//欺骗:使用`libstdc++`的实现template <typename T>static constexpr size_t accepted_index = std::__detail::__variant::__accepted_index<T, std::variant<Ts...>>;template <class F>constexpr auto with_index(F&& f) const -> decltype(auto) {return mp_with_index<sizeof...(Ts)>(index_, (F&&)f);}
public:constexpr Variant() requires std::is_default_constructible_v<Ts...[0]>//这应该有效吗:`storage_{.[:get_nth_field(0):]{}}`: storage_{.empty={}}, index_(0){std::construct_at(&storage_.[: get_nth_field(0) :]);}constexpr ~Variant() requires (std::is_trivially_destructible_v<Ts> and ...) = default;constexpr ~Variant() {if (index_ != -1) {with_index([&](auto I){std::destroy_at(&storage_.[: get_nth_field(I) :]);});}}template <typename T, size_t I = accepted_index<T&&>>requires (!std::is_base_of_v<Variant, std::decay_t<T>>)constexpr Variant(T&& t): storage_{.empty={}}, index_(-1){std::construct_at(&storage_.[: get_nth_field(I) :], (T&&)t);index_ = (int)I;}//在`P2963`前,你实际上无法很好地表达此约束constexpr Variant(Variant const&) requires (std::is_trivially_copyable_v<Ts> and ...) = default;constexpr Variant(Variant const& rhs)requires ((std::is_copy_constructible_v<Ts> and ...)and not (std::is_trivially_copyable_v<Ts> and ...)): storage_{.empty={}}, index_(-1){rhs.with_index([&](auto I){constexpr auto field = get_nth_field(I);std::construct_at(&storage_.[: field :], rhs.storage_.[: field :]);index_ = I;});}constexpr auto index() const -> int { return index_; }template <class F>constexpr auto visit(F&& f) const -> decltype(auto) {if (index_ == -1) {throw std::bad_variant_access();}return mp_with_index<sizeof...(Ts)>(index_, [&](auto I) -> decltype(auto) {return std::invoke((F&&)f,  storage_.[: get_nth_field(I) :]);});}
};

实际上,如下Variant<T,U>组成了一个存储联类型:

union Storage {Empty empty;T unnamed0;U unnamed1;~Storage() requires std::is_trivially_destructible_v<T> && std::is_trivially_destructible_v<U> = default;~Storage() { }
}

这里问题是,是否应该可如下用胶接器直接初化已定义联的成员:

: storage{.[: get_nth_field(0) :]={}}

可以说,答案应该是肯定的,此应与其他访问的工作方式一致.

3.10 结构到数组的结构

#include <meta>
#include <array>
template <typename T, std::size_t N>
struct struct_of_arrays_impl;
consteval auto make_struct_of_arrays(std::meta::info type, std::meta::info N) -> std::meta::info {std::vector<std::meta::info> old_members = nonstatic_data_members_of(type);std::vector<std::meta::info> new_members = {};for (std::meta::info member : old_members) {auto type_array = substitute(^std::array, {type_of(member), N });auto mem_descr = data_member_spec(type_array, {.name = identifier_of(member)});new_members.push_back(mem_descr);}return std::meta::define_class( substitute(^struct_of_arrays_impl, {type, N}), new_members);
}
template <typename T, size_t N>
using struct_of_arrays = [: make_struct_of_arrays(^T, ^N) :];
Example:
struct point {float x;float y;float z;
};
using points = struct_of_arrays<point, 30>;
//等价于:`构 点{std::array<float,30>x;std::array<float,30>y;std::array<float,30>z;};`

同样,可很好利用nonstatic_data_members_ofdefine_class的组合.

3.11解析命令行选项II

现在已看到了几个使用std::meta::define_class创建类型的示例,可创建一个更复杂命令行解析器示例.

这是clap的开场示例(Rust命令行参数解析器):

struct Args : Clap {Option<std::string, {.use_short=true, .use_long=true}> name;Option<int, {.use_short=true, .use_long=true}> count = 1;
};
int main(int argc, char** argv) {auto opts = Args{}.parse(argc, argv);for (int i = 0; i < opts.count; ++i) {  //`opts.count`的类型为`int`,std::print("Hello {}!", opts.name);   //`opts.name`的类型为`std::string`}
}

可以像这样实现:

struct Flags {bool use_short;bool use_long;
};
template <typename T, Flags flags>
struct Option {std::optional<T> initializer = {};//一些适合`标志`的构造器和访问器
};//按`恰好`是`适当成员`的`类型`,转换`私有类型`(其所有非静态数据成员都是`选项`的).如,如果`类型`是上面介绍的`参数`的反射,则`该函`数的计算结果将是类型的反射:`struct{std::string name;int count;}`consteval auto spec_to_opts(std::meta::info opts, std::meta::info spec) -> std::meta::info {std::vector<std::meta::info> new_members;for (std::meta::info member : nonstatic_data_members_of(spec)) {auto type_new = template_arguments_of(type_of(member))[0];new_members.push_back(data_member_spec(type_new, {.name=identifier_of(member)}));}return define_class(opts, new_members);
}
struct Clap {template <typename Spec>auto parse(this Spec const& spec, int argc, char** argv) {std::vector<std::string_view> cmdline(argv+1, argv+argc)//检查`命令行`是否包含`-help`等.struct Opts;static_assert(is_type(spec_to_opts(^Opts, ^Spec)));Opts opts;template for (constexpr auto [sm, om] : std::views::zip(nonstatic_data_members_of(^Spec), nonstatic_data_members_of(^Opts))) {auto const& cur = spec.[:sm:];constexpr auto type = type_of(om);//找到与`此选项`关联的参数auto it = std::ranges::find_if(cmdline,[&](std::string_view arg){return (cur.use_short && arg.size() == 2 && arg[0] == '-' && arg[1] == identifier_of(sm)[0])|| (cur.use_long && arg.starts_with("--") && arg.substr(2) == identifier_of(sm));});//无此参数if (it == cmdline.end()) {if constexpr (has_template_arguments(type) and template_of(type) == ^std::optional) {//`类型`是可选的,因此参数也是continue;} else if (cur.initializer) {//该类型不是可选的,但提供了初化器,请使用该opts.[:om:] = *cur.initializer;continue;} else {std::print(stderr, "Missing required option {}\n", display_string_of(sm));std::exit(EXIT_FAILURE);}} else if (it + 1 == cmdline.end()) {std::print(stderr, "Option {} for {} is missing a value\n", *it, display_string_of(sm));std::exit(EXIT_FAILURE);}//找到的参数,试解析它auto iss = ispanstream(it[1]);if (iss >> opts.[:om:]; !iss) {std::print(stderr, "Failed to parse {:?} into option {} of type {}\n",it[1], display_string_of(sm), display_string_of(type));std::exit(EXIT_FAILURE);}}return opts;}
};

3.12一个通用的格式化器

struct universal_formatter {constexpr auto parse(auto& ctx) { return ctx.begin(); }template <typename T>auto format(T const& t, auto& ctx) const {auto out = std::format_to(ctx.out(), "{}{{", has_identifier(^T) ? identifier_of(^T) : "(unnamedtype)";);auto delim = [first=true]() mutable {if (!first) {*out++ = ',';*out++ = ' ';}first = false;};template for (constexpr auto base : bases_of(^T)) {delim();out = std::format_to(out, "{}", (typename [: type_of(base) :] const&)(t));}template for (constexpr auto mem : nonstatic_data_members_of(^T)) {delim();std::string_view mem_label = has_identifier(mem) ? identifier_of(mem) : "(unnamedmember)";out = std::format_to(out, ".{}={}", mem_label, t.[:mem:]);}*out++ = '}';return out;}
};
struct B { int m0 = 0; };
struct X { int m1 = 1; };
struct Y { int m2 = 2; };
class Z : public X, private Y { int m3 = 3; int m4 = 4; };
template <> struct std::formatter<B> : universal_formatter { };
template <> struct std::formatter<X> : universal_formatter { };
template <> struct std::formatter<Y> : universal_formatter { };
template <> struct std::formatter<Z> : universal_formatter { };
int main() {std::println("{}", Z());//`Z{X{B{.m0=0},.m1=1},Y{{.m0=0},.m2=2},.m3=3,.m4=4}`
}

注意,当前不能用t.[:base:]语法访问基类子对象,即只能使用转换取基类:

static_cast<[: type_of(base) const& :]>(t), or
(typename [: type_of(base) :] const&)t

两者都必须在转换显式指定类型的常性.static_cast还必须检查权限.

3.13实现成员级hash_append

template <typename H, typename T> requires std::is_standard_layout_v<T>
void hash_append(H& algo, T const& t) {template for (constexpr auto mem : nonstatic_data_members_of(^T)) {hash_append(algo, t.[:mem:]);}
}

3.14按元组转换结构

template <typename T>
constexpr auto struct_to_tuple(T const& t) {constexpr auto members = nonstatic_data_members_of(^T);constexpr auto indices = []{std::array<int, members.size()> indices;std::ranges::iota(indices, 0);return indices;}();constexpr auto [...Is] = indices;return std::make_tuple(t.[: members[Is] :]...);
}

或:

consteval auto type_struct_to_tuple(info type) -> info {return substitute(^std::tuple,nonstatic_data_members_of(type)| std::views::transform(std::meta::type_of)| std::views::transform(std::meta::type_remove_cvref)| std::ranges::to<std::vector>());
}
template <typename To, typename From, std::meta::info ... members>
constexpr auto struct_to_tuple_helper(From const& from) -> To {return To(from.[:members:]...);
}
template<typename From>
consteval auto get_struct_to_tuple_helper() {using To = [: type_struct_to_tuple(^From): ];std::vector args = {^To, ^From};for (auto mem : nonstatic_data_members_of(^From)) {args.push_back(reflect_value(mem));}/*或,使用区间:args.append_range(nonstatic_data_members_of(^From)| std::views::transform(std::meta::reflect_value));*/return extract<To(*)(From const&)>(substitute(^struct_to_tuple_helper, args));
}
template <typename From>
constexpr auto struct_to_tuple(From const& from) {return get_struct_to_tuple_helper<From>()(from);
}

在此,type_struct_to_tuple带类似struct{T t;U const &u;V v;},并返回std::tuple<T,U,V>反射类型.这给了类型.

然后,struct_to_tuple_helper实际转换函数模板,可按非类型模板参数包来实现成员的所有反射.

这是个常式函数,而不是常值函数,因为一般,转换是运行时操作.

但是,确定需要struct_to_tuple_helper实例是个编译时操作,且必须使用常值函数处理(因为该函数调用nonstatic_data_members_of),因此需要单独的get_struct_to_tuple_helper()函数模板.

替代所有内容放在一起,创建需要的struct_to_tuple_helper的实例化,并用提取取得该实例编译时引用.

因此可简单调用f是指向struct_to_tuple_helper正确私有的函数引用.

3.15实现tuple_cat

template<std::pair<std::size_t, std::size_t>... indices>
struct Indexer {template<typename Tuples>//可用元组索引而不是元组的元组auto operator()(Tuples&& tuples) const {using ResultType = std::tuple<std::tuple_element_t<indices.second,std::remove_cvref_t<std::tuple_element_t<indices.first, std::remove_cvref_t<Tuples>>>>...>;return ResultType(std::get<indices.second>(std::get<indices.first>(std::forward<Tuples>(tuples)))...);}
};
template <class T>
consteval auto subst_by_value(std::meta::info tmpl, std::vector<T> args)-> std::meta::info
{std::vector<std::meta::info> a2;for (T x : args) {a2.push_back(std::meta::reflect_value(x));}return substitute(tmpl, a2);
}
consteval auto make_indexer(std::vector<std::size_t> sizes)-> std::meta::info
{std::vector<std::pair<int, int>> args;for (std::size_t tidx = 0; tidx < sizes.size(); ++tidx) {for (std::size_t eidx = 0; eidx < sizes[tidx]; ++eidx) {args.push_back({tidx, eidx});}}return subst_by_value(^Indexer, args);
}
template<typename... Tuples>
auto my_tuple_cat(Tuples&&... tuples) {constexpr typename [: make_indexer({type_tuple_size(type_remove_cvref(^Tuples))...}) :] indexer;return indexer(std::forward_as_tuple(std::forward<Tuples>(tuples)...));
}

3.16命名元组

实现命名元组的难点,实际上是按非类型模板参数对待串.因为不能只传递"x"auto V形式的非类型模板参数,所以有两个方法来指定组成部分:

可引入类型,这样就可以写make_named_tuple<pair<int,"x">,pair<double,"y">>()
一直反射,这样就可以写:

make_named_tuple<^int, std::meta::reflect_value("x"), ^double, std::meta::reflect_value("y")>()

当前不支持拼接串字面,且给定合适的fixed_string类型,方法遵守define_class已显示的类似模式:

template <class T, fixed_string Name>
struct pair {static constexpr auto name() -> std::string_view { return Name.view(); }using type = T;
};
template <class... Tags>
consteval auto make_named_tuple(std::meta::info type, Tags... tags) {std::vector<std::meta::info> nsdms;auto f = [&]<class Tag>(Tag tag){nsdms.push_back(data_member_spec(dealias(^typename Tag::type),{.name=Tag::name()}));};(f(tags), ...);return define_class(type, nsdms);
}
struct R;
static_assert(is_type(make_named_tuple(^R, pair<int, "x">{}, pair<double, "y">{})));
static_assert(type_of(nonstatic_data_members_of(^R)[0]) == ^int);
static_assert(type_of(nonstatic_data_members_of(^R)[1]) == ^double);
int main() {[[maybe_unused]] auto r = R{.x=1, .y=2.0};
}

或,可在域中保存所有内容,来避免非类型模板参数的问题:

consteval auto make_named_tuple(std::meta::info type, std::initializer_list<std::pair<std::meta::info, std::string_view>> members) {std::vector<std::meta::data_member_spec> nsdms;for (auto [type, name] : members) {nsdms.push_back(data_member_spec(type, {.name=name}));}return define_class(type, nsdms);
}
struct R;
static_assert(is_type(make_named_tuple(^R, {{^int, "x"}, {^double, "y"}})));
static_assert(type_of(nonstatic_data_members_of(^R)[0]) == ^int);
static_assert(type_of(nonstatic_data_members_of(^R)[1]) == ^double);
int main() {[[maybe_unused]] auto r = R{.x=1, .y=2.0};
}

3.17编译时票据计数器

此处建议的特征使得在编译时更新票证计数器更容易.这不是理想的实现(更喜欢直接支持编译时,即常值,变量),但它展示了编译时如何搞出可变状态.

class TU_Ticket {template<int N> struct Helper;
public:static consteval int next() {int k = 0;//搜索下个不完整`'Helper<k>"`.std::meta::info r;while (is_complete_type(r = substitute(^Helper, { std::meta::reflect_value(k) })))++k;//定义`'Helper<k>'`并返回其索引.define_class(r, {});return k;}
};
constexpr int x = TU_Ticket::next();
static_assert(x == 0);
constexpr int y = TU_Ticket::next();
static_assert(y == 1);
constexpr int z = TU_Ticket::next();
static_assert(z == 2);

3.18模拟反射类型

尽管认为单个不透明的std::meta::info类型是最好的,且对反射最具可扩展性,但承认SG7表达了对未来支持"类型反射"的愿望.

下面演示了一种由不同类型表示不同类的反射组装类型反射库的可能方法,及此处建议的工具.

//表示判定限制的`'std::meta::info'`.
template <std::meta::info Pred>requires (std::predicate<[:type_of(Pred):], std::meta::info>)
struct metatype {std::meta::info value;//除非满足`判定`,否则`构造`的格式是错误的.consteval metatype(std::meta::info r) : value(r) {if (![:Pred:](r))throw "Reflection is not a member of this metatype";}//转为`'std::meta::info'`允许拼接此类型的值.consteval operator std::meta::info() const { return value; }static consteval bool check(std::meta::info r) { return [:Pred:](r); }
};//表示"匹配失败"已知元类型的类型.
struct unmatched {consteval unmatched(std::meta::info) {}static consteval bool check(std::meta::info) { return true; }
};
//用`更具描述性的类型`,返回给定`"更富有"`反射.
template <typename... Choices>
consteval std::meta::info enrich(std::meta::info r) {//因为控制类型,所以知道第一个构造器是取`信息`的构造器.在}处添加了复制/移动构造器,因此是列表中的最后构造器.  std::array ctors = {*(members_of(^Choices) | std::views::filter(std::meta::is_constructor)).begin()...,*(members_of(^unmatched) | std::views::filter(std::meta::is_constructor)).begin()};std::array checks = {^Choices::check..., ^unmatched::check};for (auto [check, ctor] : std::views::zip(checks, ctors))if (extract<bool>(reflect_invoke(check, {reflect_value(r)})))return reflect_invoke(ctor, {reflect_value(r)});std::unreachable();
}

利用此机制,根据按参数提供的反射"类型"来选择不同重载函数.

using type_t = metatype<^std::meta::is_type>;
using template_t = metatype<^std::meta::is_template>;
//对不同反射"类型",重载函数的示例.
void PrintKind(type_t) { std::println("type"); }
void PrintKind(template_t) { std::println("template"); }
void PrintKind(unmatched) { std::println("unknown kind"); }
int main() {//按以下值之一分类反射:`Type,Function`或`Unmatched`.auto enrich = [](std::meta::info r) { return ::enrich<type_t, template_t>(r); };//演示如何使用`'变富'`来选择重载.PrintKind([:enrich(^metatype):]);                   //`"template"`PrintKind([:enrich(^type_t):]);                     //`"type"`PrintKind([:enrich(std::meta::reflect_value(3):]);  //`"unknownkind"`
}

注意,可按包装字面类型的,或包装可能不同类型多个值泛化元型类.
如,这可用来根据以下因子选择编译时重载:两个整数是否共享相同奇偶校验,可选中是否有值,变量(variant)任意(any)持有的值的类型,或编译时串的语法形式.

C++23中以相同泛型实现相同目标,需要两次拼写参数:第一次取得模板参数的"分类标签",然后再次调用函数,即

Printer::PrintKind<classify(^int)>(^int).
//或更糟......
fn<classify(Arg1, Arg2, Arg3)>(Arg1, Arg2, Arg3).

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/470681.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【算法】——二分查找合集

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 零&#xff1a;二分查找工具 1&#xff1a;最基础模版 2&#xff1a;mid落点问题 一&#xff1a;最…

读数据质量管理:数据可靠性与数据质量问题解决之道03数据目录

1. 同步数据 1.1. 不同的数据仓库和数据湖通过数据集成层来进行桥接 1.2. AWS Glue、Fivetran和Matillion等数据集成工具从不同来源收集数据&#xff0c;统一这些数据&#xff0c;并将其转换为上游来源 1.3. 数据集成的一个典型用例是收集数据湖的数据并以结构化格式将其加载…

openSUSE 环境下通过 zypper 安装软件

操作场景 为了提升您在云服务器上的软件安装效率&#xff0c;减少下载和安装软件的成本&#xff0c;腾讯云提供了 zypper 下载源。openSUSE 操作系统和部分 SLES 的云服务器用户可通过 zypper 快速安装软件。本文档以 openSUSE 操作系统为例&#xff0c;指导您通过 zypper 快速…

ima.copilot-腾讯智能工作台

一、产品描述 ima.copilot是腾讯推出的基于腾讯混元大模型技术的智能工作台&#xff0c;通过先进的人工智能技术&#xff0c;为用户提供了一个全新的搜读写体验&#xff0c;让知识管理变得更加智能和高效。它不仅是一个工具&#xff0c;更是一个智能的伙伴&#xff0c;能够帮助…

NVIDIA Isaac Sim 仿真平台体验测评

目录 一、引言二、GPU加速相关体验2.1 Isaac Sim GPU 加速体验2.2 GPU加速体验分析 三、AI框架集成相关体验四、学术研究价值五、开发生态六、综合分析6.1 主要优势6.1.1 仿真效率6.1.2 开发便利性6.1.3 与 AI 框架的协同性 6.2 潜在应用场景 七、运行体验与建议7.1 GPU加速与P…

WebRTC API分析

主题 本文详细描述常用的webrtc api 媒体协商类 myPeerConnection.createOffer([options]); var options { offerToReceiveAudio: true, // 告诉另一端&#xff0c;你是否想接收音频&#xff0c;默认true offerToReceiveVideo: true, // 告诉另一端&a…

11张思维导图带你快速学习java

博主主页:【南鸢1.0】 本文专栏&#xff1a;JAVA 本文目录 简介 1.Java SE​编辑 2.Java Web 3.MySQL​编辑 4.前端技术 5.Linux 6.Spring SpringMvc mybatis 7.JVM 8.Springboot 9.Vue 10.SpringCloud 11.常用中间件 总结 简介 Java是一种跨平台的编程语言&am…

Jmeter基础篇(22)服务器性能监测工具Nmon的使用

一、前言 我们在日常做压测的过程中&#xff0c;不仅仅需要监控TPS&#xff0c;响应时间&#xff0c;报错率等这些系统基础性能数据&#xff0c;还需要对服务器的性能&#xff08;如CPU、磁盘、内存、网络IO等&#xff09;做监控&#xff0c;以求对系统运行过程中的硬件性能有…

Unity3D学习FPS游戏(12)敌人检测和攻击玩家

前言&#xff1a;上一篇实现了敌人能动&#xff0c;有了点乐趣&#xff0c;但是敌人和玩家没什么对抗性。本篇将实现敌人追击玩家&#xff0c;并攻击玩家。 敌人攻击玩家 敌人检测玩家目标思路-碰撞检测的Trigger触发实现 敌人攻击目标思路-模仿玩家发射子弹的思路实现 效果 敌…

利用滑动窗口解题

目录 前言&#xff1a; 第一题&#xff1a;209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 第二题&#xff1a;1004. 最大连续1的个数 III - 力扣&#xff08;LeetCode&#xff09; 第三题&#xff1a;3. 无重复字符的最长子串 - 力扣&#xff08;LeetCode&…

车载空气净化器语音芯片方案

开发背景&#xff1a; 随着人们生活质量的不断提升和环保意识的日益增强&#xff0c;车内空气质量成为了广大车主关注的焦点。长时间封闭的车厢环境&#xff0c;加之城市空气污染、新车内饰材料释放的有害气体等因素&#xff0c;使得车内空气质量往往不尽如人意&#xff0c;严重…

《MYSQL45讲》误删数据怎么办

对误删数据分类的话&#xff0c;有 1.delete 误删行 2.drop table 或者truncate table 语句误删表 3.使用drop database 误删数据库 4.使用rm命令误删整个MYSQL实例 一&#xff0c;误删行 一下操作前置条件是&#xff1a;binlog的格式是row&#xff0c;并且binglog_row_im…

不对称信息

你买了一辆二手车&#xff0c;你并不知道它出过几次事故&#xff0c;但它之前的车主却对此了如指掌。来买保险的公司都是那些出险概率很大的&#xff08;比如矿工、化工厂&#xff09;&#xff0c;但那些安全的公司很少去买保险&#xff0c;这两种问题都属于信息不对称问题。 …

94个属于一区且接受医工交叉领域投稿的期刊汇总|个人观点·24-11-13

小罗碎碎念 继汇总病理AI的基础模型、病理组学&影像组学的公开数据集以后&#xff0c;我们再来盘一盘医工交叉领域有哪些热门期刊可以投稿。我会分区进行介绍&#xff0c;每个区则会进一步划分学科种类&#xff0c;方便大家选择适合自己的投稿期刊。 这期推文先分享大类属…

网站小程序app怎么查有没有备案?

网站小程序app怎么查有没有备案&#xff1f;只需要官方一个网址就可以&#xff0c;工信部备案查询官网地址有且只有一个&#xff0c;百度搜索 "ICP备案查询" 找到官方gov.cn网站即可查询&#xff01; 注&#xff1a;网站小程序app备案查询&#xff0c;可通过输入单位…

MySQL45讲 第二十讲 幻读是什么,幻读有什么问题?

文章目录 MySQL45讲 第二十讲 幻读是什么&#xff0c;幻读有什么问题&#xff1f;一、幻读的定义二、幻读带来的问题&#xff08;一&#xff09;语义问题&#xff08;二&#xff09;数据一致性问题 三、InnoDB 解决幻读的方法四、总结 MySQL45讲 第二十讲 幻读是什么&#xff0…

FatLab:我的编程课程系列

FatLab 是一款教程类软件。 大概是因为我的编程生涯始于自学&#xff0c;FatLab便也保持了这种气息&#xff1a;从一个“自然生长”的角度提供了一套C语言教程。 教程方面&#xff0c;目前仅完成了《C语言基础要素》系列。正如其名&#xff0c;这个系列仅探讨了语言中非常基础…

冗余连接2 hard题 代随C#写法

此题在卡码网109与力扣685题亦有记载 有一说一C#写法我没咋搞懂 就看明白了思路 这里贴一个答案待后续我醒悟了再来看罢 难就难在对整体数据结构classUnion&#xff08;并查集&#xff09;的理解不熟并且 对于输入输出这个迭代过程理解上也比较吃力 109. 冗余连接II 题…

【QT】QSS

个人主页~ 一、QSS QSS可以说是拿了CSS的一部分过来用&#xff0c;是CSS的简化版本 1、基本语法 选择器 {属性名:属性值; }将界面上所有的QPushButton文本颜色都改为红色 QPushButton {color:red; }2、设置方式 &#xff08;1&#xff09;指定控件样式设置 在widget.cpp中…

java模拟键盘实现selenium上下左右键 table中的左右滚动条实现滚动

在这篇文章中&#xff0c;我们将学习如何使用Java编程语言模拟键盘输入&#xff0c;特别是模拟上下左右方向键的操作。这是一个很有趣的项目&#xff0c;尤其适合刚入行的开发者。我们将分步进行&#xff0c;接下来&#xff0c;我们会通过表格展示整个实现过程&#xff0c;然后…