如何用C++20实现概念约束:结构体非静态公有成员均为非const引用
实现C++20概念约束结构体所有非静态公有数据成员为非const引用
核心思路说明
标准C20本身没有内置机制可以枚举任意类型的所有非静态公有数据成员,因此我们需要针对聚合类型(满足C聚合体定义的结构体/类)来实现约束——因为聚合体的非静态数据成员均为公有,且可通过编译器扩展或结构化绑定技巧访问成员类型。
依赖编译器扩展的实现(GCC/Clang)
如果可以接受GCC/Clang的编译器扩展,我们可以利用__builtin_member_count获取聚合体的成员数量,结合模板元编程检查每个成员类型是否为非const左值引用:
代码实现
#include <type_traits> #include <utility> // 检查单个类型是否为非const左值引用 template<typename T> constexpr bool is_non_const_lvalue_reference_v = std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>; // 递归检查tuple的所有元素是否符合非const左值引用要求 template<typename Tuple> struct all_non_const_refs : std::true_type {}; template<typename First, typename... Rest> struct all_non_const_refs<std::tuple<First, Rest...>> : std::bool_constant<is_non_const_lvalue_reference_v<First> && all_non_const_refs<std::tuple<Rest...>>::value> {}; template<typename Tuple> constexpr bool all_non_const_refs_v = all_non_const_refs<Tuple>::value; // 利用GCC扩展获取聚合体的成员类型tuple template<typename T> constexpr auto get_member_types() { if constexpr (std::is_aggregate_v<T>) { return []<std::size_t... Is>(std::index_sequence<Is...>) { return std::tuple<std::decay_t<decltype(std::get<Is>(std::declval<T>()))>...>(); }(std::make_index_sequence<__builtin_member_count(T)>()); } else { return std::tuple<>(); } } template<typename T> using member_types_t = decltype(get_member_types<T>()); // 主判断trait template<typename T> constexpr bool isStructOfNonConstReferences_v = std::is_aggregate_v<T> && all_non_const_refs_v<member_types_t<T>>; // C++20概念定义 template<typename T> concept StructOfNonConstReferences = isStructOfNonConstReferences_v<T>; // 测试示例 struct A { int& i; std::string& s; }; struct B { int& i; std::string& s; void foo() { s = std::to_string(i); } }; struct C { int i; std::string& s; }; struct D { const int& i; const std::string& s; }; static_assert(isStructOfNonConstReferences<A>); static_assert(isStructOfNonConstReferences<B>); static_assert(!isStructOfNonConstReferences<C>); static_assert(!isStructOfNonConstReferences<D>);
纯标准C++20的受限实现
如果必须使用纯标准C++20,我们只能通过结构化绑定的重载来支持有限数量的成员(比如最多支持10个成员),手动处理不同成员数量的情况:
代码实现
#include <type_traits> // 检查单个类型是否为非const左值引用 template<typename T> constexpr bool is_non_const_lvalue_reference_v = std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>; // 检查空结构体 template<typename T> constexpr bool check_members(T) requires (std::is_aggregate_v<T>) { return true; } // 检查1个成员的情况 template<typename T> constexpr bool check_members(T t) requires requires { auto [a] = t; requires is_non_const_lvalue_reference_v<decltype(a)>; } { auto [a] = t; return true; } // 检查2个成员的情况 template<typename T> constexpr bool check_members(T t) requires requires { auto [a, b] = t; requires is_non_const_lvalue_reference_v<decltype(a)> && is_non_const_lvalue_reference_v<decltype(b)>; } { auto [a, b] = t; return true; } // 可继续添加3个、4个...成员的检查重载,直到满足需求上限 // 主判断trait template<typename T> constexpr bool isStructOfNonConstReferences_v = std::is_aggregate_v<T> && check_members(std::declval<T>()); // C++20概念定义 template<typename T> concept StructOfNonConstReferences = isStructOfNonConstReferences_v<T>; // 测试示例 struct A { int& i; std::string& s; }; struct B { int& i; std::string& s; void foo() {} }; struct C { int i; std::string& s; }; struct D { const int& i; const std::string& s; }; static_assert(isStructOfNonConstReferences<A>); static_assert(isStructOfNonConstReferences<B>); static_assert(!isStructOfNonConstReferences<C>); static_assert(!isStructOfNonConstReferences<D>);
关键注意事项
- 上述实现仅针对聚合类型:如果结构体包含私有/保护数据成员、用户声明的构造函数或基类,则不属于聚合体,无法被正确检查。
- 成员函数不影响检查结果:符合示例中结构体B的情况,因为我们只约束数据成员的类型。
- 空结构体默认符合条件:根据逻辑,空结构体没有违反约束的成员,因此
isStructOfNonConstReferences_v会返回true,如果需要排除空结构体,可以在trait中添加std::tuple_size_v<member_types_t<T>> > 0的判断。
内容的提问来源于stack exchange,提问作者PluginPenguin
相关产品推荐
相关产品推荐

