使用c++11 constexpr时遇到的坑详解
最近在使用constexpr的时候无意中踩了个小坑。
下面给个小示例:
#include <iostream> constexpr int n = 10; constexpr char *msg = "hello, world!"; int main() { for (auto i = 0; i < n; ++i) { std::cout << msg << std::endl; } }
constexpr应该是大家很熟悉的东西了,也是最常用的c++11新特性之一。和宏相比除了更强的类型安全之外,constexpr还带来了编译期计算。
上面的代码相当简单,我们循环输出“hello, world!”这个字符串10次。
这么简单的代码还有讨论的必要吗?一开始我也是这么想的,然而当我们编译运行的时候却会得到下面这样的警告:
$ g++ --version g++ (gcc) 10.2.0 copyright (c) 2020 free software foundation, inc. this is free software; see the source for copying conditions. there is no warranty; not even for merchantability or fitness for a particular purpose. $ g++ -std=c++17 -wall -wextra test.cpp test.cpp:4:23: 警告: iso c++ forbids converting a string constant to ‘char*' [-wwrite-strings] 4 | constexpr char *msg = "hello, world!"; | ^~~~~~~~~~~~~~~
这段信息的意思是c++不允许把字符串字面量赋值给char*。
然而对于constexpr,中是这么写的:
a constexpr specifier used in an object declaration implies const.
这里的object你可以理解为变量,意思是constexpr修饰的变量都会隐式添加一个const限定符。
也就是说:
// t 是任意类型 constexpr t a = xxx; // 不考虑其他因素,在类型上等价于: t const a = xxx;
我们这里的t实际上可以填任意类型,包括指针。这不是说明我们的指针变量有const吗?
眼尖的读者大概已经知道答案了:constexpr添加的是顶层const。
所以我们的代码实际上是这样的:
// 原本的代码 constexpr char *msg = "hello, world!"; // 实际上的效果 char * const msg = "hello, world!";
下面一行的msg实际上是一个指向char的指针常量,而我们可以通过它任意修改被指向的字符串(当然这是未定义行为)。指针常量意味着我们不能把这个指针重新指向其他的对象,这个const作用在指针本身上,因此叫做顶层const。
而字符串常量的类型是const char[n],在表达式里退化为const char *,这表示一个指向常量字符串的指针,这里的const的底层的,因为它作用于被指向的对象而不是我们的指针自身。
对于顶层const,赋值的时候是可以被去除的,而底层const则不行,这就是为什么编译器会弹出警告的原因了。
正确的做法也很简单,牢记constexpr不是const的等价替代品,它只会添加顶层const,不会添加底层const。
所以constexpr的字符串常量应该这样写:
constexpr const char *p = "hello, world!";
或者你的编译环境支持c++17,我更推荐你这样写:
#include <string_view> constexpr std::string_view msg = "hello, world!";
使用string_view之后就不会出现上面的顶层/底层const的坑了。所以在现代c++里能不用裸指针就尽量不要用。
参考
https://*.com/questions/54258241/warning-iso-c-forbids-converting-a-string-constant-to-char-for-a-static-c
总结
到此这篇关于使用c++11 constexpr时遇到坑的文章就介绍到这了,更多相关c++11 constexpr坑内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
推荐阅读
-
Laravel Intervention/image图片处理扩展包的安装、使用与可能遇到的坑详解
-
使用git bash编译安装sysbench时遇到的坑
-
详解如何解决使用JSON.stringify时遇到的循环引用问题
-
详解vscode使用git所遇到的坑
-
详解react-native-fs插件的使用以及遇到的坑
-
详解Node.js 中使用 ECDSA 签名遇到的坑
-
SpringBoot使用logback自定义配置时遇到的坑 --- 在 /tmp目录下自动生成spring.log文件
-
使用c++11 constexpr时遇到的坑详解
-
Laravel Intervention/image图片处理扩展包的安装、使用与可能遇到的坑详解
-
记一次在node.js中使用crypto的createCipheriv方法进行加密时所遇到的坑