实现支持无额外参数的类sprintf风格std::string格式化函数
解决sprintf风格格式化函数无参数调用的编译警告问题
我需要实现一个类sprintf风格的std::string格式化函数,写出的代码如下:
#include <memory> #include <string> #include <stdexcept> template<typename ... Args> std::string string_format( const std::string& format, Args ... args ) { int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0' if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); } auto size = static_cast<size_t>( size_s ); std::unique_ptr<char[]> buf( new char[ size ] ); std::snprintf( buf.get(), size, format.c_str(), args ... ); return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside }
但调用string_format("just a string");这种仅传入字符串、无额外参数的场景时,会触发编译警告:"Not a string literal and no format arguments"。
我尝试写了一个前置拦截函数pre_string_format,想通过参数数量分支处理,但编译器无法识别逻辑,代码如下:
template<typename ... Args> std::string pre_string_format( const std::string& format, Args ... args) { auto s = sizeof...(Args); if( s > 0 ) { return string_format( format, std::forward<Args> (args)...); } else { std::string retStr = format; return retStr; } }
我的底层需求是实现一个拦截printf的打印函数,在打印前对字符串做追加操作,同时将内容记录到不同文件。原方案调整后大部分场景效果良好,但无参数调用的场景失效。
可行解决方案
方案1:重载无参数版本的string_format
直接添加一个非模板的重载函数,专门处理无额外参数的情况:
std::string string_format(const std::string& format) { return format; }
当调用string_format("just a string");时,编译器会优先匹配这个非模板重载,不会触发原模板函数中snprintf的警告。
方案2:用if constexpr实现编译期分支(C++17及以上)
修改原模板函数,利用C++17的if constexpr在编译期判断参数数量,避免无参数时调用snprintf:
#include <memory> #include <string> #include <stdexcept> template<typename ... Args> std::string string_format( const std::string& format, Args ... args ) { if constexpr (sizeof...(Args) == 0) { return format; } else { int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); } auto size = static_cast<size_t>( size_s ); std::unique_ptr<char[]> buf( new char[ size ] ); std::snprintf( buf.get(), size, format.c_str(), args ... ); return std::string( buf.get(), buf.get() + size - 1 ); } }
if constexpr会在编译期确定执行哪个分支,无参数时直接返回原字符串,完全绕过snprintf的调用,从根源消除警告。
原拦截函数失效原因
原pre_string_format中的普通if是运行期判断,编译器仍会实例化else分支里的string_format调用。当Args为空时,string_format的模板实例还是会调用snprintf,因此警告无法避免。
内容的提问来源于stack exchange,提问作者scottc5
相关产品推荐
相关产品推荐

