如何用C++折叠表达式实现带分隔符的可变参数打印?
用折叠表达式实现带分隔符的可变参数模板打印函数
问题描述
尝试用折叠表达式替代模板递归编写可变参数模板打印函数,当前代码:
template <typename... Ts, typename charT, typename traits> constexpr std::basic_ostream<charT, traits>& many_print(std::basic_ostream<charT, traits>& os, Ts... args){ os << '{'; (os << ... << args); return os << '}'; }
调用many_print(std::cout, 1, 2);时输出{12},期望输出{1, 2}。
尝试过的写法会导致分隔符前置:
template <typename... Ts, typename charT, typename traits> constexpr std::basic_ostream<charT, traits>& many_print(std::basic_ostream<charT, traits>& os, Ts... args){ os << '{'; (os << ... << ((void)(os << ", "), args)); return os << '}'; }
输出为{, 1, 2},不符合需求。需要无需模板递归的解决方案,哪怕能输出{1, 2, }也可接受。
解决方案
1. 完美无末尾分隔符的实现(推荐)
结合折叠表达式和lambda表达式,通过标记是否为第一个参数来控制分隔符输出,逻辑清晰且无递归:
#include <iostream> #include <utility> // 用于std::forward template <typename... Ts, typename charT, typename traits> constexpr std::basic_ostream<charT, traits>& many_print(std::basic_ostream<charT, traits>& os, Ts&&... args){ os << '{'; bool is_first = true; // 定义lambda处理单个参数的打印,自动处理分隔符 auto print_arg = [&](auto&& arg) { if (!is_first) { os << ", "; } is_first = false; os << std::forward<decltype(arg)>(arg); }; // 用折叠表达式遍历所有参数,调用lambda打印 (print_arg(std::forward<Ts>(args)), ...); return os << '}'; } // 处理无参数的重载版本 template <typename charT, typename traits> constexpr std::basic_ostream<charT, traits>& many_print(std::basic_ostream<charT, traits>& os){ return os << "{}"; }
调用示例:
int main() { many_print(std::cout, 1, "hello", 3.14); // 输出 {1, hello, 3.14} many_print(std::cout); // 输出 {} return 0; }
2. 允许末尾带分隔符的简化实现
如果可以接受末尾的逗号,写法更简洁:
template <typename... Ts, typename charT, typename traits> constexpr std::basic_ostream<charT, traits>& many_print(std::basic_ostream<charT, traits>& os, Ts&&... args){ os << '{'; // 右折叠打印每个参数+逗号 (os << ... << (std::forward<Ts>(args) << ", ")); return os << '\b}'; // 用退格符去掉最后一个逗号(仅终端输出有效) // 若无需去除末尾逗号,直接 return os << '}'; 即可,输出如 {1, 2, } }
注意:退格符\b仅在终端输出时能覆盖末尾逗号,若输出到文件或其他流,建议直接保留末尾逗号,或者结合if constexpr处理空参数情况。
内容的提问来源于stack exchange,提问作者Nathan29006781
相关产品推荐
相关产品推荐

