如何以标准方式保存constexpr std::string函数的返回结果?
编译期生成枚举字符串并复用缓冲区的问题
C++允许constexpr函数返回std::string,比如我们可以借助magic_enum库实现一个生成所有枚举值空格分隔字符串的函数:
template <typename E> static constexpr std::string GetAllEnumNamesAsString() { const std::string space = " "; constexpr std::array names = magic_enum::enum_names<E>(); std::string all{ names[0] }; for (int i = 1; i < names.size(); i++) all += (space + std::string{ names[i] }); return all; }
但这里存在核心问题:虽然constexpr函数内部可以使用std::string,但由于它依赖堆内存,我们无法直接声明constexpr std::string变量,只能将返回值设为const。原本预期编译器会把constexpr函数计算出的最终字符串缓冲区放入静态存储,但GCC和MSVC都会把整个调用逻辑委托给运行时,生成大量冗余代码。
比如以下代码:
int main() { // 这部分必须在编译期执行,因为static_assert要求编译期常量 static_assert(GetAllNames<MyEnum>().length() == 17); // 但编译器把这里的调用处理成纯运行时操作,甚至不会先把最终字符串放入静态存储再复制到堆 const std::string names = GetAllNames<MyEnum>(); printf("%s\n", names.c_str()); return 0; }
为了实际利用编译器在编译期计算出的字符串缓冲区,我们可以把constexpr中临时的std::string内容复制到静态存储的容器(比如std::array)里。但因为变量不能作为模板参数,目前的方案需要两次调用函数:
template <size_t Length> class FakeStringView { public: constexpr FakeStringView(const std::string_view& s1) : _array{} { for (auto i = 0; i < s1.length(); ++i) _array[i] = s1[i]; } constexpr std::string_view Get() const { return { _array.data(), _array.size() }; } private: std::array<char, Length> _array; }; template <typename E> constexpr auto GetAllNames() { // 两次调用:一次获取长度作为模板参数,一次获取字符串内容 return FakeStringView<GetAllNamesImpl<E>().length()>(GetAllNamesImpl<E>()); } constexpr auto fakestringview = GetAllNames<MyEnum>(); std::cout << fakestringview.Get();
当前的疑问是:是否已有标准方案或更优的实现方式,能避免重复调用constexpr函数、浪费编译期计算成果?
内容的提问来源于stack exchange,提问作者user450775
相关产品推荐
相关产品推荐

