为何无法将字符串字面量传入const auto&类型非类型模板参数(NTTP)
问题
先看最小复现代码:
template<const auto& A> class B {}; int main() { B<"abc"> b; return 0; }
上述代码无论怎么调整变体都无法编译通过,但CTRE库却支持类似写法:
constexpr auto match(std::string_view sv) noexcept { return ctre::match<"h.*">(sv); }
该库的模板同样声明了const auto&类型形参,参数经过接收、类型别名定义、转发、最终使用的完整链路都没有编译错误。
自行实现同类逻辑时同样遇到编译报错,相关代码如下:
template<const auto& StringParameterConstant, size_t IndexParameterConstant = 0> consteval size_t string_length() { if constexpr(StringParameterConstant[IndexParameterConstant] != '\0') return string_length<StringParameterConstant, IndexParameterConstant + 1>(); else return IndexParameterConstant; } template<char... CharacterParameterConstants> struct TemplateString { constexpr static const size_t length = sizeof...(CharacterParameterConstants); constexpr static const char array[length] = { CharacterParameterConstants... }; }; template< const auto& StringParameterConstant, size_t IndexParameterConstant, size_t LengthParameterConstant, char... CharacterParameterConstants > consteval auto to_template_string() { //... } template<const auto& StringParameterConstant> using TemplateStringType = decltype(to_template_string //...
template< const auto& PatternParameterConstant, const auto& NameParameterConstant, //... > struct RegexTerm { using PatternType = TemplateStringType<PatternParameterConstant>; using NameType = TemplateStringType<NameParameterConstant>; constexpr static ctpg::regex_term<PatternType::string> term( // OK NameType::string, // OK //... ); }; //... // 尝试过constexpr static、放在consteval/constexpr函数内等写法均失败 RegexTerm<"[1-9][0-9]*", "Numerical">::term; // 编译错误
解答
编译失败核心原因
报错本质是违反了C++非类型模板参数(NTTP)的绑定规则:
- 声明为
const auto&的引用类型模板形参,要求绑定的实参必须是静态存储期、编译期可唯一确定身份的常量对象。 - 直接传入的字符串字面量本质是编译器临时生成的const char数组,标准没有规定同内容字符串字面量的存储唯一性,编译器无法在模板实例化阶段确认其固定身份,因此不允许直接绑定到引用类型NTTP,触发编译错误。
CTRE的实现原理
CTRE并没有直接将原始字符串字面量绑定到引用形参,它做了两层关键转换绕过了这个限制:
- 首先定义了一个符合C++20类类型NTTP要求的固定长度字符串结构体,类似你实现的
TemplateString,这类字面量类型可以按值作为模板参数传递,不需要绑定引用。 - 库自定义了consteval字符串字面量运算符,你写的
"h.*"作为模板参数传入时,会先被这个运算符转换成上述固定长度字符串结构体的编译期常量,后续链路里的const auto&形参,绑定的都是这个已经确定身份的静态常量,完全符合语法要求。
修复方案
不需要复刻CTRE的完整复杂链路,两种简单改法即可解决问题:
- 移除参数的引用修饰:C++20已经原生支持数组、字面量结构体作为值类型NTTP,直接把模板形参从
const auto&改为auto,就可以直接接收字符串字面量,不需要额外处理存储期问题,改造成本最低。 - 增加字面量转换层:如果必须保留引用类型形参,自行实现一个consteval字面量运算符,将传入的原始字符串构造成静态存储期的固定长度字符串常量,再将这个常量传入模板即可。
内容的提问来源于stack exchange,提问作者cgbsu
相关产品推荐
相关产品推荐

