能否用std::apply与元组实现检查成员函数存在的Concept?
问题描述
我正在编写结构体自动反射的简易代码,每个结构体通过静态元组存储成员名称与成员指针,使用std::apply遍历元组完成操作。为减少缺失getDBType辅助函数时的编译错误规模,我希望实现一个Concept,需要完成以下逻辑:
- 遍历结构体的静态元组
- 获取元组元素中的成员类型
- 检查该类型对应的
getDBType函数是否存在
但当前实现未达预期:当getDBType函数缺失时,直接出现编译错误而非Concept不满足。尝试了多种lambda与std::apply的组合均无效,询问是否可通过元组与std::apply实现该Concept。
解决方案
可以实现目标Concept,但不能直接在Concept中使用std::apply(会触发硬编译错误,而非SFINAE友好的失败)。正确的思路是:先定义检查单个类型是否存在getDBType的辅助Concept,再通过折叠表达式遍历元组的所有元素,逐个验证该辅助Concept是否满足。
核心要点:
- 利用
std::tuple_size和std::tuple_element遍历元组类型,避免编译时的std::apply调用触发硬错误 - 折叠表达式可以简洁地对元组所有元素做逻辑与检查
- 单个类型的
getDBType检查要做成SFINAE友好的,比如用requires表达式直接尝试调用函数
示例代码
#include <tuple> #include <string> // 辅助函数示例:存在getDBType的类型 std::string getDBType(int) { return "INT"; } std::string getDBType(std::string) { return "VARCHAR"; } // 检查单个类型是否有对应的getDBType函数的Concept template<typename T> concept HasDBType = requires(T t) { { getDBType(t) } -> std::convertible_to<std::string>; }; // 检查元组中所有元素的成员类型都满足HasDBType的辅助模板 template<typename Tuple> struct TupleAllHasDBType; template<typename... Ts> struct TupleAllHasDBType<std::tuple<Ts...>> { static constexpr bool value = (HasDBType<typename Ts::MemberType> && ...); }; // 针对结构体的Concept:要求存在静态元组成员,且元组所有元素的成员类型都有getDBType template<typename T> concept ReflectableWithDBType = requires { typename decltype(T::members)::value_type; { TupleAllHasDBType<decltype(T::members)>::value } -> std::convertible_to<bool>; } && TupleAllHasDBType<decltype(T::members)>::value; // 测试结构体:所有成员类型都有getDBType struct User { int id; std::string name; struct Member1 { static constexpr const char* name = "id"; using MemberType = int; static constexpr auto ptr = &User::id; }; struct Member2 { static constexpr const char* name = "name"; using MemberType = std::string; static constexpr auto ptr = &User::name; }; static constexpr auto members = std::make_tuple(Member1{}, Member2{}); }; // 测试结构体:存在无getDBType的成员类型 struct BadUser { int id; float score; // 无对应的getDBType struct Member1 { static constexpr const char* name = "id"; using MemberType = int; static constexpr auto ptr = &BadUser::id; }; struct Member2 { static constexpr const char* name = "score"; using MemberType = float; static constexpr auto ptr = &BadUser::score; }; static constexpr auto members = std::make_tuple(Member1{}, Member2{}); }; // 测试用函数:仅接受满足ReflectableWithDBType的类型 template<ReflectableWithDBType T> void processDB(T) {} int main() { processDB(User{}); // 编译通过 // processDB(BadUser{}); // 编译失败,提示不满足ReflectableWithDBType Concept }
关键说明
- 单个类型检查:
HasDBTypeConcept直接在requires中尝试调用getDBType(T{}),如果调用合法则Concept满足,否则SFINAE友好地失败。 - 元组遍历检查:通过
TupleAllHasDBType模板,利用折叠表达式(HasDBType<typename Ts::MemberType> && ...)对元组所有元素的MemberType做逻辑与检查,确保所有成员类型都符合要求。 - 结构体Concept:
ReflectableWithDBType先检查结构体是否有members静态元组,再验证元组所有元素的成员类型都满足HasDBType。
这样当某个成员类型缺失getDBType时,编译器会提示Concept不满足,而非大量无关的编译错误,达到缩小错误规模的目的。
内容的提问来源于stack exchange,提问作者urbanek.p.j
相关产品推荐
相关产品推荐

