如何实现C++20中view::format("{}: {}")的直观语法?
实现直观语法的自定义范围视图:
views::format("{}: {}") 需求背景
使用C++20开发,依赖range-v3库处理范围,希望构建自定义范围视图,自动将zip_view等返回的元组传入fmt::format,期望的直观调用语法如下:
std::vector v1{"a", "b", "c", "d"}; std::vector v2{"alpha", "beta", "gamma", "delta"}; fmt::print( "", fmt::join(views::zip(v1, v2) | views::format("{}: {}"), "\n") );
已知可通过views::transform手动提取元组参数传入fmt::format,但希望消除此类样板代码。此前尝试两种方案:
- 用
consteval函数实现时,因非类型模板参数非常量表达式报错; - 用字符串字面量模板参数实现了功能,但调用语法
views::format2<"{}: {}">()不够直观,目标是实现views::format("{}: {}")的写法。
解决方案
核心思路是利用字符串字面量作为函数参数,通过模板推导捕获编译期常量字符串,结合range-v3的适配器机制实现。具体步骤如下:
1. 定义编译期字符串包装模板
先实现一个能捕获字符串字面量的模板类型,将运行时传入的字面量转为编译期可用的常量:
template <std::size_t N> struct StringLiteral { constexpr StringLiteral(const char (&str)[N]) noexcept { std::copy_n(str, N, value); } char value[N]; }; // 类模板推导指引,自动推导字符串长度 template <std::size_t N> StringLiteral(const char (&)[N]) -> StringLiteral<N>;
2. 实现format视图适配器
基于range-v3的adaptor_base定义适配器,接受StringLiteral参数并生成对应的transform逻辑:
namespace views { namespace detail { template <StringLiteral FormatStr> struct format_fn { template <typename Tuple> auto operator()(Tuple&& t) const { // 用std::apply自动展开元组参数到fmt::format return std::apply( [](auto&&... args) { return fmt::format(FormatStr.value, std::forward<decltype(args)>(args)...); }, std::forward<Tuple>(t) ); } }; // 外层适配器,接收字符串字面量并返回绑定格式串的transform视图 struct format_adaptor : ranges::adaptor_base { template <StringLiteral FormatStr> constexpr auto operator()() const { return ranges::views::transform(format_fn<FormatStr>{}); } }; } // 全局可调用的format视图对象 inline constexpr detail::format_adaptor format; }
3. 验证调用语法
现在可以按照期望的直观语法调用:
std::vector v1{"a", "b", "c", "d"}; std::vector v2{"alpha", "beta", "gamma", "delta"}; fmt::print( "{}\n", fmt::join(views::zip(v1, v2) | views::format("{}: {}"), "\n") ); // 输出结果: // a: alpha // b: beta // c: gamma // d: delta
原理说明
StringLiteral模板通过构造函数捕获字符串字面量,借助C++20的类模板推导自动确定字符串长度,将字面量转为编译期常量;format_adaptor的operator()接收StringLiteral参数,返回绑定了对应格式化字符串的transform视图,内部用std::apply自动展开元组参数,彻底消除手动提取元组元素的样板代码;- 该方案既保留了fmt库编译期检查格式串与参数匹配的安全性,又实现了简洁直观的函数式调用语法。
注意事项
- 仅支持传入字符串字面量,若传入运行时生成的字符串会触发编译错误,这符合fmt库编译期校验的设计初衷;
- 需确保编译环境支持C++20及以上标准,且正确链接range-v3和fmt库。
内容的提问来源于stack exchange,提问作者Michael A
相关产品推荐
相关产品推荐

