C++临时对象指针作const char*模板参数报错及递归类型推导问题
编译失败问题复现
尝试将临时类对象的内部资源作为模板参数传入时编译失败,测试代码如下:
#include <iostream> constexpr size_t size(const char* s) { int i = 0; while(*s!=0) { ++i; ++s; } return i; } template <const char* S> struct string_struct { constexpr string_struct() { for (int i=0; i < size(S); ++i) { buf_[i] = S[i]; } } constexpr const char* get() { return buf_; } char buf_[size(S)] = {0}; }; constexpr const char some_chars[] = "100"; constexpr auto compose_string() { string_struct<some_chars> other_str{}; return string_struct<other_str.get()>; } int main() { compose_string(); }
GCC 12.1编译报错信息:
<source>: In function 'constexpr auto compose_string()': <source>:32:41: error: 'other_str.string_struct<(& some_chars)>::get()' is not a valid template argument for 'const char*' because it is not the address of a variable 32 | return string_struct<other_str.get()>; | ^ <source>:29:16: error: invalid return type 'auto' of 'constexpr' function 'constexpr auto compose_string()' 29 | constexpr auto compose_string() |
待解决问题:
- 如何将
string_struct实例的内部资源用于模板实例化? - 若方案可行,如何推导递归返回多层嵌套
string_struct实例的函数的返回类型?该实现思路是否符合C++标准、具备可行性?
问题解答
原实现思路不可行的核心原因
C++标准对指针类型非类型模板参数有强制约束:仅指向静态存储期、具备内部/外部链接的对象的指针,可作为合法的非类型模板参数。
原代码中other_str.get()返回的是函数局部栈对象other_str的内部成员buf_的地址,属于自动存储期对象,地址随函数调用栈变化,完全不满足模板参数的要求,因此编译直接失败。不存在直接将局部对象内部缓冲区地址作为模板参数的方法,这是标准层面的硬限制。
符合标准的替代实现方案
放弃将字符串内容绑定到外部const char*模板参数的思路,改用C++20支持的类类型非类型模板参数(NTTP),直接将字符序列作为模板参数的一部分持有,无需依赖外部指针:
#include <array> #include <cstddef> #include <utility> template<std::size_t N> struct string_struct { std::array<char, N> buf{}; constexpr string_struct() = default; // 从字符串字面量构造 constexpr string_struct(const char (&s)[N]) { for (std::size_t i = 0; i < N; ++i) buf[i] = s[i]; } // 拼接两个字符串的内部构造函数 template<std::size_t M, std::size_t... Is, std::size_t... Js> constexpr string_struct(const string_struct<M>& a, const string_struct<N-M+1>& b, std::index_sequence<Is...>, std::index_sequence<Js...>) : buf{a.buf[Is]..., b.buf[Js]..., '\0'} {} constexpr const char* get() const { return buf.data(); } constexpr std::size_t length() const { return N - 1; } }; // 字符串字面量推导指引 template<std::size_t N> string_struct(const char (&)[N]) -> string_struct<N>; // 字符串拼接运算符重载 template<std::size_t N, std::size_t M> constexpr auto operator+(const string_struct<N>& a, const string_struct<M>& b) { return string_struct<N + M - 1>(a, b, std::make_index_sequence<N-1>{}, std::make_index_sequence<M-1>{}); } // 递归拼接示例 constexpr auto compose_string() { constexpr string_struct s1{"100"}; constexpr string_struct s2{"abc"}; constexpr string_struct s3{"def"}; // 多层嵌套拼接可在编译期完成,返回类型自动推导 return s1 + s2 + s3; } #include <iostream> int main() { constexpr auto res = compose_string(); std::cout << res.get() << std::endl; // 输出:100abcdef }
该实现完全符合C++20标准,无未定义行为:
- 字符串内容直接存储为模板参数的一部分,不依赖外部静态存储的指针
- 支持任意多层递归拼接、嵌套扩展
- 所有操作可在constexpr上下文完成,编译期即可生成最终字符串结果
递归返回类型推导说明
C++14及以上版本中,constexpr函数使用auto作为返回值类型是合法语法。原代码中invalid return type 'auto'报错是前置的模板参数非法导致的连锁错误,并非auto本身的问题。
上述替代实现中,每一步拼接操作的返回值都是长度确定的string_struct<固定长度>实例,类型在编译期完全确定,编译器可自动推导返回类型,无需额外手动声明类型。
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

