You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

编译时获取可变参数模板的索引与类型,实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 09:10:30