编译时获取可变参数模板的索引与类型,实现pqxx查询结果的std::tuple构造
编译时获取可变参数模板的索引与类型,实现pqxx查询结果的std::tuple构造
这个问题我之前也踩过坑!要解决编译时把pqxx的查询行转成对应类型的std::tuple,核心就是要同时拿到可变参数模板里每个类型的编译时索引,彻底避开运行时变量带来的未定义行为。
最优解决方案(C++17及以上)
直接用C++17支持的模板lambda配合std::index_sequence,不需要额外写辅助函数,代码简洁又安全:
template <typename... Rets, typename... Args> std::tuple<Rets...> call_nn(std::string_view query, Args... args) { pqxx::work w(con_); pqxx::row res = w.exec_params1(pqxx::zview(query), args...); w.commit(); // 这里就是你要的return语句 return [&res]<std::size_t... Is>(std::index_sequence<Is...>) { return std::tuple<Rets...>(res[Is].as<Rets>()...); }(std::index_sequence_for<Rets...>{}); }
原理说明
std::index_sequence_for<Rets...>会自动生成一个和Rets参数包长度完全匹配的编译时索引序列:比如Rets是int, std::string, double,它就会生成std::index_sequence<0,1,2>- 模板lambda接收这个索引序列后,
Is...会展开成对应的每个索引值,和Rets...一一对应展开,最终就会生成你想要的res[0].as<int>(), res[1].as<std::string>(), res[2].as<double>()这种形式 - 所有操作都是编译时完成的,没有任何运行时开销,也完全避免了之前
i++导致的未定义行为
兼容C++14的方案(无模板lambda)
如果你的项目还在使用C++14,可以写一个简单的辅助函数:
// 辅助函数:把pqxx::row转成对应类型的tuple template<typename... Rets, std::size_t... Is> std::tuple<Rets...> row_to_tuple(const pqxx::row& res, std::index_sequence<Is...>) { return std::tuple<Rets...>(res[Is].as<Rets>()...); } template <typename... Rets, typename... Args> std::tuple<Rets...> call_nn(std::string_view query, Args... args) { pqxx::work w(con_); pqxx::row res = w.exec_params1(pqxx::zview(query), args...); w.commit(); return row_to_tuple<Rets...>(res, std::index_sequence_for<Rets...>{}); }
为什么之前的i++方案不行?
你尝试的res[i++].as<Rets>()...会触发编译器警告,本质是因为C++标准里函数参数的求值顺序是未指定的——编译器可以任意顺序展开参数包,这就导致i的递增顺序完全不确定,属于未定义行为,实际运行时可能会拿到错误的列值,必须坚决避免这种写法。
备注:内容来源于stack exchange,提问作者Daniil Rozanov
相关产品推荐
相关产品推荐

