为何func2调用报错?如何让format成为常量表达式?
首先是用于统计格式串占位符数量的constexpr函数,以及两个模板函数的完整代码:
#include <string_view> #include <iostream> constexpr int count_format_placeholders(const std::string_view format) { int count = 0; for(int i = 0; i < format.size(); ++i) { if(format[i] == '{' && format[i + 1] == '}') { ++count; ++i; } } return count; } template<typename... Args> constexpr int func(std::string_view format) { return count_format_placeholders(format); } template<typename... Args> constexpr int func2(std::string_view format) { constexpr int i = count_format_placeholders(format); return i; } int main() { constexpr std::string_view format{"{} {} {} {}:{}:{}"}; constexpr int num_placeholders = count_format_placeholders(format); // 编译通过 std::cout << num_placeholders << std::endl; constexpr int num_placeholders2 = func<int, int>(format); // 编译通过 std::cout << num_placeholders2 << std::endl; constexpr int num_placeholders3 = func2<int, int>(format); // 编译失败,提示'format is not constant expression' std::cout << num_placeholders3 << std::endl; return 0; }
错误原因
func2编译失败的核心逻辑是:函数参数不能作为编译期常量表达式使用。
尽管main中传入的format是constexpr对象,但它作为参数传递给func2后,在func2的作用域内,这个参数只是运行时常量,而非编译期常量表达式。而constexpr int i = count_format_placeholders(format);要求右侧表达式必须能在编译期求值,此时format不满足该要求,因此触发编译错误。
对比func的运行逻辑:func直接返回count_format_placeholders(format),由于func是constexpr函数,当在constexpr上下文(比如main中初始化constexpr变量)调用时,整个函数调用会被编译期求值,但这不需要函数内部的参数成为常量表达式,只是整个调用满足constexpr求值的条件。
修复方案
方案1:将format作为模板非类型参数(C++20及以上)
C++20允许std::string_view作为非类型模板参数,这样format会成为编译期已知的常量,满足内部constexpr变量的初始化要求:
// 修改func2的定义 template<std::string_view format, typename... Args> constexpr int func2() { constexpr int i = count_format_placeholders(format); return i; } // main中的调用方式 constexpr int num_placeholders3 = func2<format, int, int>();
方案2:移除内部constexpr变量,直接返回结果
如果不需要在func2内部保留编译期常量i,可以简化实现,让func2的行为与func一致,同时支持constexpr上下文的编译期求值:
template<typename... Args> constexpr int func2(std::string_view format) { return count_format_placeholders(format); }
方案3:使用consteval强制编译期求值(C++20及以上)
如果希望func2必须在编译期求值,可将其改为consteval函数,同时去掉内部的constexpr变量:
template<typename... Args> consteval int func2(std::string_view format) { return count_format_placeholders(format); }
consteval函数会强制要求所有调用都必须是编译期常量表达式,确保返回值在编译期确定,同时避免内部的常量初始化问题。
内容的提问来源于stack exchange,提问作者Eshy

