如何为constexpr C++容器实现类似push_back的追加操作?
实现constexpr版本的Shell参数解析函数
首先,你遇到的核心问题是std::vector在C++17中不支持constexpr操作(emplace_back/push_back都是运行时才能调用),而initializer_list本质是静态数组的视图,无法动态追加元素——这确实是个痛点。下面给你几个不需要第三方库的解决方案,从简单到灵活:
方案1:用std::array + 预计算参数数量(C++17兼容)
思路是先写一个constexpr函数统计输入字符串中的参数个数,再用这个数量实例化std::array,最后填充元素。这种方法完全符合C++17的constexpr要求:
#include <string_view> #include <array> constexpr size_t count_args(std::string_view const& line) { size_t count = 0; size_t pos_begin = 0, pos_end = 0; while (pos_end < line.size() && pos_end != std::string_view::npos) { pos_begin = line.find_first_not_of(" \t", pos_end); if (pos_begin == std::string_view::npos) break; pos_end = line.find_first_of(" \t", pos_begin); if (pos_end == std::string_view::npos) pos_end = line.size(); ++count; } return count; } template<size_t N> constexpr auto parse_args_impl(std::string_view const& line) { std::array<std::string_view, N> args{}; size_t idx = 0; size_t pos_begin = 0, pos_end = 0; while (pos_end < line.size() && pos_end != std::string_view::npos) { pos_begin = line.find_first_not_of(" \t", pos_end); if (pos_begin == std::string_view::npos) break; pos_end = line.find_first_of(" \t", pos_begin); if (pos_end == std::string_view::npos) pos_end = line.size(); args[idx++] = line.substr(pos_begin, pos_end - pos_begin); } return args; } // 外层函数自动推导数组大小 constexpr auto parse_args(std::string_view const& line) { return parse_args_impl<count_args(line)>(line); } // 测试用例 constexpr auto args = parse_args("ls -l /home/user"); static_assert(args[0] == "ls"); static_assert(args[1] == "-l"); static_assert(args[2] == "/home/user");
这个方案的缺点是参数数量必须在编译时确定——如果你的输入是编译期常量字符串,完全没问题;但如果是运行时输入,这个方法就不适用了(不过既然你要constexpr函数,输入本来就是编译期常量对吧?)。
方案2:自定义constexpr动态容器(C++17兼容)
如果需要更灵活的动态性(比如不想提前计算参数数),可以自己写一个极简的constexpr容器,用固定大小的缓冲区存储元素,同时跟踪有效元素数量:
#include <string_view> #include <algorithm> // 自定义constexpr容器,支持有限的动态追加 template<typename T, size_t MaxSize = 1024> struct ConstexprVector { T data[MaxSize]{}; size_t size = 0; constexpr void emplace_back(T value) { if (size >= MaxSize) return; // 可以改成编译期断言增强安全性 data[size++] = value; } // 支持范围for遍历 constexpr T* begin() { return data; } constexpr const T* begin() const { return data; } constexpr T* end() { return data + size; } constexpr const T* end() const { return data + size; } // 下标访问 constexpr T& operator[](size_t idx) { return data[idx]; } constexpr const T& operator[](size_t idx) const { return data[idx]; } }; constexpr auto parse_args(std::string_view const& line) { ConstexprVector<std::string_view> args; size_t pos_begin = 0, pos_end = 0; while (pos_end < line.size() && pos_end != std::string_view::npos) { pos_begin = line.find_first_not_of(" \t", pos_end); if (pos_begin == std::string_view::npos) break; pos_end = line.find_first_of(" \t", pos_begin); if (pos_end == std::string_view::npos) pos_end = line.size(); args.emplace_back(line.substr(pos_begin, pos_end - pos_begin)); } return args; } // 测试用例 constexpr auto args = parse_args("echo hello world"); static_assert(args.size == 3); static_assert(args[0] == "echo"); static_assert(args[1] == "hello");
这个方案的优点是用法和std::vector几乎一致,而且是完全constexpr的。你可以通过调整MaxSize模板参数来控制最大支持的参数数量,编译期就能检查是否超出限制(把return改成static_assert(size < MaxSize, "Too many args");即可)。
方案3:升级到C++20,直接用std::vector(最简单)
如果你能使用C++20或更高版本,那么std::vector已经支持constexpr操作了,只需要给原函数加上constexpr关键字即可:
#include <string_view> #include <vector> constexpr auto parse_args(std::string_view const& line){ std::vector<std::string_view> args; size_t pos_begin = 0, pos_end = 0; while (pos_end < line.size() && pos_end != std::string_view::npos) { pos_begin = line.find_first_not_of(" \t", pos_end); if (pos_begin == std::string_view::npos) break; pos_end = line.find_first_of(" \t", pos_begin); if (pos_end == std::string_view::npos) pos_end = line.size(); args.emplace_back(line.substr(pos_begin, pos_end - pos_begin)); } return args; } // 测试用例 constexpr auto args = parse_args("git commit -m \"fix bug\""); static_assert(args.size() == 4); static_assert(args[3] == "\"fix bug\"");
这个方案最省心,不需要修改太多代码,但依赖C++20标准。
内容的提问来源于stack exchange,提问作者kyb
相关产品推荐
相关产品推荐

