使用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++里能不用裸指针就尽量不要用。
参考
使用constexpr时遇到的小坑的更多相关文章
- OpenACC 计算规约时发现的小坑
▶ 使用 OpenACC 的 parallel 构件来计算规约,主要想说的是 win10 pgi 和 win10 WSL pgi 结果的不同和关于 for 循环的一个小坑 ● 正常的代码 #inclu ...
- 曲演杂坛--使用CTE时踩的小坑:No Join Predicate
在一次系统优化中,意外发现一个比较“坑”的SQL,拿出来供大家分享. 生成演示数据: --====================================== --检查测试表是否存在 IF(O ...
- JDBC 连接 MySQL 时碰到的小坑
最近从MS SQL Server换到了MySQL,已经是8.11版本了,安装的时候似乎还用了新的身份认证方式之类的,连接过程中也是磕磕绊绊,碰到很多奇奇怪怪的问题,在此记录下来. 驱动加载: 以前使用 ...
- activiti设置customSessionFactories时的一个小坑
现象:activiti设置customSessionFactories不起作用,流程还是走原来的查询方法原因:新实现的XXXEntityManagerFactory的getSessionType方法返 ...
- 注意Android里TextView控件的一个小坑,用android:theme来设置样式时动态载入的layout会丢失该样式
注意Android里TextView控件的一个小坑,用android:theme来设置样式时动态载入的layout会丢失该样式 这个坑,必须要注意呀, 比如在用ListView的时候,如果在List_ ...
- 关于CSS3中transform变换的小坑
2017年6月30日15:05:46 今天在写一个demo的时候,发现CSS3中transform变换的一个特性. 首先,我先描述一下我发现的情况(问题再现): <div class=" ...
- Vue中应用CORS实现AJAX跨域,及它在 form data 和 request payload 的小坑处理
基本概念部分(一):理解CORS 说道Vue的跨域AJAX,我想先梳理一遍CORS跨域,"跨域资源共享"(Cross-origin resource sharing),它是一个W3 ...
- mysql url 连接配置的一个小坑。 工作中不会遇到。 学习的时候会
<property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> & ...
- 关于sniff函数的一个小坑
最近在用scapy模块写一个关于WiFi的脚本时用到sniff函数,其中遇到了一个小坑,记录如下: sniff函数是在指定网卡上每次嗅探到一个数据包后然后将它传给prn指定的函数.
随机推荐
- java 递归求二叉树深度
给定二叉树,找到它的最大深度. 最大深度是从根节点到最远叶节点的最长路径上的节点数. 注意:叶子是没有子节点的节点. Example: Given binary tree [3,9,20,null,n ...
- Aibabelx-shop 大型微服务架构系列实战之技术选型
一.本项目涉及编程语言java,scala,python,涉及的技术如下: 1.微服务架构: springboot springcloud mybatisplus shiro 2.全文检索技术 sol ...
- Android Studio 之 通过 Intent 完成点击按钮实现页面跳转
•Intent 简介 Intent 是 Android 程序中各组件之间进行交互的一种重要方式: 它不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据. Intent 有多个构造函数,其 ...
- Go-25-文件管理
FileInfo接口 package main import ( "fmt" "os" ) // FileInfo 接口文件的信息包括文件名.文件大小.修改权限 ...
- Go-06-数据类型、常量、运算符
数据类型转换 Go语言采用数据类型前置加括号的方式进行类型转换,格式如:T(表达式).T表示要转换的类型:表达式包括变量.数值.函数返回值等. var a int =100 b := float(a) ...
- 横趟!面试中遇到的 ZooKeeper 问题
本文作者:HelloGitHub-老荀 本文是 HelloZooKeeper 系列的最后一篇文章,接下来主要聊聊面试中如果被问到 ZooKeeper 的问题如何回答,也可以当作学完本系列的测试. 准备 ...
- PBR(基于物理的渲染)学习笔记2
相关资料 https://www.cnblogs.com/dojo-lzz/p/13237686.html 文档:PBR学习笔记.note 链接:http://note.youdao.com/note ...
- centos7安装kubernetes k8s 1.18
可以参考其他网友的阿里云搭建k8s高可用集群(1.17.3) https://www.cnblogs.com/gmmy/p/12372805.html 准备四台centos7虚拟机,用来安装k8s集群 ...
- GridSearchCV 参数
GridSearchCV(estimator, param_grid, scoring=None, fit_params=None, n_jobs=1, iid=True, refit=True, c ...
- 深入探索Android热修复技术原理读书笔记 —— 热修复技术介绍
1.1 什么是热修复 对于广大的移动开发者而言,发版更新是最为寻常不过的事了.然而,如果你 发现刚发出去的包有紧急的BUG需要修复,那你就必须需要经过下面这样的流程: 这就是传统的更新流程,步骤十分繁 ...