如何在C++中通过参数包展开实现链式成员函数调用?
用参数包实现链式成员函数调用(以nlohmann json为例)
很多库(比如nlohmann json)的类型支持通过成员函数/运算符返回自身实例,实现链式调用,比如:
auto my_data = my_json_object["first key"]["second key"];
完全可以通过参数包实现自动展开链式调用,替代手动编写多层运算符调用。以下是几种可行的实现方式:
1. 递归展开参数包(兼容C++11及以上)
通过递归模板,逐步处理每个参数,每一步都调用目标运算符,再递归处理剩余参数:
#include <nlohmann/json.hpp> using json = nlohmann::json; // 基础终止条件:无额外参数时直接返回原对象 template <class Json> auto get_elem(Json&& json) { return std::forward<Json>(json); } // 递归处理参数包:先处理第一个键,再递归处理剩下的键 template <class Json, class FirstKey, class... RestKeys> auto get_elem(Json&& json, FirstKey&& first_key, RestKeys&&... rest_keys) { return get_elem( std::forward<Json>(json)[std::forward<FirstKey>(first_key)], std::forward<RestKeys>(rest_keys)... ); } // 使用示例 int main() { json my_json_object = { {"first key", {{"second key", "hello world"}}} }; auto my_data = get_elem(my_json_object, "first key", "second key"); // my_data == "hello world" }
这种方式兼容性强,C++11就能使用,运行时无额外开销,仅编译阶段会生成递归展开的代码。
2. C++17折叠表达式(更简洁)
利用C++17引入的折叠表达式,一行代码完成参数包的展开,写法更紧凑:
#include <nlohmann/json.hpp> using json = nlohmann::json; template <class Json, class... Keys> auto get_elem(Json&& json, Keys&&... keys) { auto result = std::forward<Json>(json); // 左折叠表达式:依次将每个键应用到当前结果上 (result = result[std::forward<Keys>(keys)], ...); return result; }
折叠表达式会自动展开为result = result[key1]; result = result[key2]; ...,最终效果和手动链式调用完全一致。
3. 优化:支持const对象与避免拷贝
如果需要和原生链式调用完全一致(比如返回引用而非拷贝,支持const对象),可以重载const和非const版本的函数:
#include <nlohmann/json.hpp> using json = nlohmann::json; // const版本:返回const引用,避免修改原对象 template <class... Keys> const auto& get_elem(const json& json, Keys&&... keys) { const auto* current = &json; ((*current = &(*current)[std::forward<Keys>(keys)]), ...); return *current; } // 非const版本:返回可修改的引用 template <class... Keys> auto& get_elem(json& json, Keys&&... keys) { auto* current = &json; ((*current = &(*current)[std::forward<Keys>(keys)]), ...); return *current; }
这种实现可以避免不必要的对象拷贝,完全复刻原生链式调用的行为。
内容的提问来源于stack exchange,提问作者xzaton jw
相关产品推荐
相关产品推荐

