39 | 如何在编译期玩转字符串?
吴咏炜
该思维导图由 AI 生成,仅供参考
你好,我是吴咏炜。
在实际的项目里,我遇到过不少在编译期处理字符串的需求。今天,我们就来把这个问题好好讨论一下。
对象的选择
在编译期处理字符串,你是不能使用 std::string 的。原因有以下几个方面:
在 C++20 之前你完全无法在编译期使用 string。而且,对于编译期 string 的支持来得比较晚,只有 MSVC 较早支持,GCC 需要刚出炉不久的 GCC 12,Clang 需要当前(2022 年 6 月)尚未正式发布的 LLVM 15。
到了 C++20,你虽然可以在编译期使用 string,但实际上仍有很多不方便的地方。最明显的,编译期生成的字符串不能在运行期使用。并且,string 不可以声明为 constexpr。
string 不能用作模板参数。
因此我们只能放弃这个看起来最方便的方式,另外探索一条新路。我们的基本操作对象可以是下面这几样:
常字符指针,这是字符串字面量会自然退化成的东西
string_view,C++17 里新增的有力工具,方法和 string 类似,且基本都是 constexpr
array,使用它我们才可以返回全新的字符串
我们的编译期字符串处理,也因此会围绕着这几种类型来进行讨论。
常见操作
获取字符串长度
一个最最基本的操作,显然就是获取字符串的长度。这里,我们不能使用 C 的 strlen 函数,因为这个函数不是 constexpr。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了在编译期处理字符串的方法和技巧,重点介绍了使用常字符指针、`string_view`和`array`的方法。作者详细讨论了获取字符串长度、查找字符、字符串比较和截取子串等常见操作的实现方式。此外,还分享了实际项目中遇到的问题和解决方案,以及不同版本编译器对代码生成的影响。文章还介绍了在参数传递中将字符串作为类型使用的方法,包括GCC的非标准扩展和C++20的标准方法。最后,作者提出了使用宏定义统一接口的建议,以便在实际项目中使用这些功能的代码是统一的。总的来说,本文对编译期字符串处理的方法和技巧进行了深入浅出的介绍,对于需要在编译期处理字符串的读者具有一定的参考价值。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《现代 C++ 编程实战》,新⼈⾸单¥59
《现代 C++ 编程实战》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(4)
- 最新
- 精选
- 李云龙分享一下我的思考题解决方案:我原本想着用递归来写,但编译一直没有通过。最后我只能用手动展开的方法,写了一个最多支持5层目录的路径前缀分离,除了下面这个函数之外,其他函数都与老师的保持一致。 template <CARG Str> constexpr auto remove_head_5(Str cpath) { constexpr auto path = CARG_UNWRAP(cpath); constexpr int skip = (*path == '/') ? 1 : 0; constexpr auto pos = path + skip; constexpr auto start = find(pos, '/'); if constexpr (start == nullptr) { return copy_str<length(pos)>(pos); } else { constexpr auto pos2 = start + 1; constexpr auto start2 = find(pos2, '/'); if constexpr (start2 == nullptr) { return copy_str<length(pos2)>(pos2); } else { constexpr auto pos3 = start2 + 1; constexpr auto start3 = find(pos3, '/'); if constexpr (start3 == nullptr) { return copy_str<length(pos3)>(pos2); } else { constexpr auto pos4 = start3 + 1; constexpr auto start4 = find(pos4, '/'); if constexpr (start4 == nullptr) { return copy_str<length(pos4)>(pos4); } else { return copy_str<length(start4 + 1)>(start4 + 1); } } } } } int main() { //调用 puts(::remove_head_5(CARG_WRAP("/usr/local/test/txt/file")).data()); return 0; }
作者回复: 有限制还是不好。实现一个 rfind(需要给出长度,或者调用编译期可执行的 strlen)或者 find_last_of 就可以了。原理上没有特别之处。
2023-11-20归属地:北京21 - 李云龙老师,这段代码的两个return返回不同的array类型,但可以编译通过,是因为pos和start已经是编译期常量了,在编译期就可以确定走哪个return分支,从而就可以确定函数的返回值类型了。我的理解正确吗? if constexpr (start == nullptr) { return copy_str<length(pos)>(pos); } else { return copy_str<length(start + 1)>(start + 1); }
作者回复: 对,如果 start 是普通的函数参数那就不行了。
2023-11-19归属地:美国1 - 行大运什么时候出一个完整的C++20专栏!
作者回复: 目前没有计划……
2023-01-24归属地:广东 - piboyec++20 字符串处理,可以超越c了
作者回复: C++ 字符串处理有哪里不如 C?
2022-09-06归属地:上海4
收起评论