C++20:如何约束参数包为可构造/转换为string的类型?
实现C++20的URL参数拼接函数
append_params 1. 定义约束参数的Concept
首先我们需要定义三个Concept,分别对应「可构造std::string」「可通过std::to_string转换」,以及二者的组合:
#include <string> #include <memory_resource> #include <utility> #include <tuple> #include <cstddef> // 判断类型能否通过std::string构造函数生成字符串 template<typename T> concept StringConstructible = requires(const T& t) { std::string(t); }; // 判断类型能否通过std::to_string转换为字符串 template<typename T> concept StringConvertibleViaToStr = requires(const T& t) { std::to_string(t); }; // 组合约束:满足任意一个转换条件即可 template<typename T> concept Stringable = StringConstructible<T> || StringConvertibleViaToStr<T>;
2. 编译时选择参数转换方式
编写辅助函数,利用if constexpr在编译时自动选择最优的字符串转换方式(构造函数或std::to_string):
// 编译时决策的字符串转换辅助函数 template<Stringable T> std::string to_string_helper(const T& val) { if constexpr (StringConstructible<T>) { return std::string(val); } else { return std::to_string(val); } }
3. 实现支持参数包的append_params函数
通过折叠表达式约束参数包的每个元素都满足Stringable,同时处理URL的拼接逻辑:
基础版本(直接拼接参数)
template<typename... Ts> requires (Stringable<Ts> && ...) std::pmr::string append_params(std::pmr::string url, Ts&&... args) { // 仅当存在参数时才进行拼接 if constexpr (sizeof...(args) > 0) { // 处理URL的前缀:无?则加?,有?则加& url += (url.find('?') == std::pmr::string::npos) ? '?' : '&'; // 折叠表达式遍历所有参数,逐个转换并拼接 auto append_single = [&url](const auto& arg) { url += to_string_helper(arg); url += '&'; }; (append_single(std::forward<Ts>(args)), ...); // 移除最后多余的& url.pop_back(); } return url; }
进阶版本(处理键值对参数)
如果需要拼接key=val形式的键值对参数,可以调整为要求参数包长度为偶数,并分组处理:
// 辅助函数:分组处理键值对参数 template<std::size_t... Is, typename... Args> void append_kv_pairs(std::pmr::string& url, std::index_sequence<Is...>, Args&&... args) { bool first_pair = true; (([&](){ if (!first_pair) url += '&'; first_pair = false; // 取第2*Is个参数作为键,2*Is+1个作为值 const auto& key = std::get<2*Is>(std::forward_as_tuple(args...)); const auto& val = std::get<2*Is + 1>(std::forward_as_tuple(args...)); url += to_string_helper(key); url += '='; url += to_string_helper(val); }()), ...); } // 支持键值对的append_params template<typename... Ts> requires (sizeof...(Ts) % 2 == 0) && (Stringable<Ts> && ...) std::pmr::string append_params(std::pmr::string url, Ts&&... args) { if constexpr (sizeof...(args) > 0) { url += (url.find('?') == std::pmr::string::npos) ? '?' : '&'; // 生成索引序列,处理每一对参数 append_kv_pairs(url, std::make_index_sequence<sizeof...(args)/2>{}, std::forward<Ts>(args)...); } return url; }
关键语法说明
- 参数包约束:
(Stringable<Ts> && ...)是C++20的折叠表达式,它会展开为Stringable<T1> && Stringable<T2> && ... && Stringable<Tn>,确保参数包的每个元素都满足约束。 - 编译时分支:
if constexpr用于在编译时判断条件,避免运行时分支开销,确保只生成对应转换方式的代码。
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

