编译时获取printf对应类型格式化参数的代码问题排查
问题:编译时获取printf格式化符逻辑错误
我编写了一段Lambda代码,意图在编译时返回printf及同类函数对应的字符串格式化符,但未按预期运行。当指定类型为unsigned char时,当前输出为%llu,而预期输出应为%hhu。
原始代码
#include <array> #include <string_view> #include <iostream> int main() { using T = unsigned char; constexpr std::string_view fmt = [&]() -> std::string_view { std::tuple<char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long> dummy; constexpr std::size_t map_size = std::tuple_size_v<decltype(dummy)>; constexpr std::size_t idx = [&]<std::size_t... Is>(std::index_sequence<Is...>) { std::size_t ret = 0; ([&]{ ret = Is; return std::same_as<T, decltype(std::get<Is>(dummy))>; }() || ...); return ret; }(std::make_index_sequence<map_size>{}); return std::array<std::string_view, map_size>{ "%hhd", "%hhu", "%hd", "%hu", "%d", "%u", "%ld", "%lu", "%lld", "%llu" }[idx]; }(); std::cout << fmt << std::endl; }
当前输出
%llu
预期结果
%hhu
问题原因
核心问题出在折叠表达式的执行逻辑:
([&]{ ret = Is; return std::same_as<T, decltype(std::get<Is>(dummy))>; }() || ...)
这段代码会先将ret赋值为当前索引Is,再判断类型是否匹配。由于逻辑或||的短路特性是"条件为真时停止",但这里的赋值操作在判断之前执行,导致即使前面的类型匹配,后续的索引依然会覆盖ret的值,最终ret被设为最后一个索引(9),对应%llu。
修复方案
调整逻辑,仅当类型匹配时才赋值ret,并利用短路求值停止后续循环:
修复后的完整代码
#include <array> #include <string_view> #include <iostream> #include <tuple> int main() { using T = unsigned char; constexpr std::string_view fmt = []() -> std::string_view { using DummyTuple = std::tuple<char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long>; constexpr std::size_t map_size = std::tuple_size_v<DummyTuple>; constexpr std::size_t idx = []<std::size_t... Is>(std::index_sequence<Is...>) { std::size_t ret = 0; // 类型匹配时赋值并触发短路求值 ([&]{ if constexpr (std::same_as<T, std::tuple_element_t<Is, DummyTuple>>) { ret = Is; return true; } return false; }() || ...); return ret; }(std::make_index_sequence<map_size>{}); return std::array<std::string_view, map_size>{ "%hhd", "%hhu", "%hd", "%hu", "%d", "%u", "%ld", "%lu", "%lld", "%llu" }[idx]; }(); std::cout << fmt << std::endl; }
关键优化点
- 用
DummyTuple类型别名替代临时变量dummy,通过std::tuple_element_t直接获取类型,避免不必要的变量创建 - 使用
if constexpr确保类型判断在编译时完成 - 调整折叠表达式逻辑:仅当类型匹配时才赋值
ret,并返回true触发短路求值,终止后续循环
修复后效果
指定unsigned char类型时,代码会正确返回%hhu。
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

