使用std::source_location::current()的模板代码编译失败咨询
问题背景
在探索std::source_location::current()的用法时,我看到Stack Overflow某线程的采纳答案中的代码,使用x86-64 gcc 13.1编译器,搭配-O3 -std=c++20 -Wall -Wextra -Wpedantic编译选项在Godbolt上运行时无法编译,想知道是否操作有误,若不是,为何该代码会被采纳为答案?
原代码如下:
#include <format> #include <iostream> #include <source_location> template<typename... Targs, auto location = std::source_location::current()> auto tprintf(char const* format, Targs const&... args) -> void { std::cout << std::format("{}:{}: ", location.file_name(), location.line()) << std::format(format, args...); } auto main() -> int { tprintf("hello {}", "world"); // prints "example.cpp:12: hello world" }
编译失败原因
你没有操作错误,编译失败是因为GCC 13.1对C++20中std::source_location作为模板默认参数的支持存在兼容性问题。虽然C++20标准允许将std::source_location作为模板非类型参数的默认值,但GCC 13.1在处理auto类型的模板参数推导时,无法正确识别std::source_location::current()的返回类型并完成绑定,导致编译报错。
代码被采纳的原因
这段代码的核心逻辑完全符合C++20标准,在**对该特性支持完善的编译器(如Clang 14+、MSVC 2022+)**下可以正常编译运行。Stack Overflow的答案被采纳,通常是因为其思路正确、贴合标准需求:它利用模板默认参数的特性,在函数调用点自动捕获当前的源位置信息,无需用户手动传递std::source_location参数,这正是std::source_location设计的典型用法之一,能简化日志、调试类代码的编写。
可兼容GCC 13.1的修正代码
有两种方式可以让代码在GCC 13.1上正常编译:
方式1:将source_location作为函数默认参数
把std::source_location从模板参数移到函数参数列表,作为默认参数:
#include <format> #include <iostream> #include <source_location> template<typename... Targs> auto tprintf(char const* format, Targs const&... args, const std::source_location& location = std::source_location::current()) -> void { std::cout << std::format("{}:{}: ", location.file_name(), location.line()) << std::format(format, args...); } auto main() -> int { tprintf("hello {}", "world"); // 输出对应文件行号和内容 }
方式2:显式指定模板参数类型
将模板参数的auto替换为明确的std::source_location类型,避免编译器推导:
#include <format> #include <iostream> #include <source_location> template<typename... Targs, std::source_location location = std::source_location::current()> auto tprintf(char const* format, Targs const&... args) -> void { std::cout << std::format("{}:{}: ", location.file_name(), location.line()) << std::format(format, args...); } auto main() -> int { tprintf("hello {}", "world"); }
这两种写法都能在GCC 13.1下正常编译,同时保留原代码的核心功能:自动捕获调用点的源文件和行号信息。
内容的提问来源于stack exchange,提问作者spaL

