GCC 10及以上版本constexpr函数编译失败问题求助
GCC版本兼容性问题:字符串字面量传递给constexpr函数的编译错误
代码片段
#include <iostream> // 接收const char数组引用的另一个函数 template <size_t N> constexpr int anotherFunction(const char (&str)[N]) { int ct = 0; for(int i=0; i< N; i++) { ct++; } return ct; } // 模拟另一个包装器的功能 #define WRAP(sev, str, ...) do { constexpr int ans = anotherFunction(str); std::cout << ans << std::endl;} while(0); // 接收字符串字面量并传递给另一个函数的函数模板 template <size_t N, typename... Args> void printString(const int& log, const char (&str)[N], Args&&... args) { WRAP(0, str, std::forward<Args>(args)...); } int main() { printString(0, "Hello, World! %s %d", 1, "hello"); return 0; }
编译错误信息
<source>:25:53: required from here <source>:15:48: error: 'str' is not a constant expression 15 | #define WRAP(sev, str, ...) do { constexpr int ans = anotherFunction(str); std::cout << ans << std::endl;} while(0); | ^~~ <source>:19:5: note: in expansion of macro 'WRAP' 19 | WRAP(0, str, std::forward<Args>(args)...); | ^~~~ Compiler returned: 1
问题背景
这段代码在GCC 9及之前版本可正常编译,但从GCC 10开始触发上述编译错误。此代码模拟了项目中封装第三方宏的场景。
编辑说明:我知道此处ans不一定需要是constexpr,但该宏实际来自第三方接口,后续会将其作为std::array的大小,因此必须保持constexpr。希望不修改WRAP宏,仅调整printString()函数,使其能将字符串字面量正确传递给宏。
编辑补充:值得注意的是,这段代码在GCC 14中可成功编译运行!
解决方案
核心思路
GCC 10开始严格遵循C++标准:函数模板的形参(即使是数组引用类型)属于函数参数,不属于编译期常量表达式,无法在constexpr上下文直接使用。要解决问题,需将字符串字面量以模板非类型参数的形式传递,确保其在编译期可见。
方法1:C++17及以下兼容方案
由于C++17不支持直接将字符串字面量作为非类型模板参数,需先定义全局常量字符串:
#include <iostream> template <size_t N> constexpr int anotherFunction(const char (&str)[N]) { int ct = 0; for(int i=0; i< N; i++) { ct++; } return ct; } #define WRAP(sev, str, ...) do { constexpr int ans = anotherFunction(str); std::cout << ans << std::endl;} while(0); // 修改后的printString:将字符串作为模板非类型参数 template <const char* str, typename... Args> void printString(const int& log, Args&&... args) { WRAP(0, str, std::forward<Args>(args)...); } // 定义全局常量字符串 constexpr char hello_str[] = "Hello, World! %s %d"; int main() { // 通过模板参数传递字符串 printString<hello_str>(0, 1, "hello"); return 0; }
方法2:C++20及以上优化方案
C++20支持字符串字面量作为非类型模板参数,可直接传递无需额外定义变量:
#include <iostream> #include <string_view> template <size_t N> constexpr int anotherFunction(const char (&str)[N]) { int ct = 0; for(int i=0; i< N; i++) { ct++; } return ct; } #define WRAP(sev, str, ...) do { constexpr int ans = anotherFunction(str); std::cout << ans << std::endl;} while(0); // 使用std::string_view作为模板参数 template <std::string_view str, typename... Args> void printString(const int& log, Args&&... args) { // 传递str.data(),匹配原函数的const char数组引用 WRAP(0, str.data(), std::forward<Args>(args)...); } int main() { // 直接传递字符串字面量作为模板参数 printString<"Hello, World! %s %d">(0, 1, "hello"); return 0; }
方法3:贴合原数组引用逻辑的C++20方案
如果希望保留原数组引用的类型匹配,可使用C++20的扩展非类型模板参数:
#include <iostream> template <size_t N> constexpr int anotherFunction(const char (&str)[N]) { int ct = 0; for(int i=0; i< N; i++) { ct++; } return ct; } #define WRAP(sev, str, ...) do { constexpr int ans = anotherFunction(str); std::cout << ans << std::endl;} while(0); // 直接将数组引用作为模板非类型参数 template <size_t N, const char (&str)[N], typename... Args> void printString(const int& log, Args&&... args) { WRAP(0, str, std::forward<Args>(args)...); } int main() { printString<sizeof("Hello, World! %s %d"), "Hello, World! %s %d">(0, 1, "hello"); return 0; }
原因解析
- GCC 9及之前版本存在宽松的编译期常量判断,允许将函数参数(数组引用)视为常量表达式使用。
- GCC 10开始严格执行C++标准:函数参数不属于常量表达式上下文,无法在
constexpr初始化中使用。 - GCC 14修复了相关兼容性问题,重新允许模板参数传递的数组引用在
constexpr上下文使用,因此代码可正常编译。
内容的提问来源于stack exchange,提问作者Archisman Pathak
相关产品推荐
相关产品推荐

