使用c++11 constexpr时遇到的坑详解

 更新时间:2021年05月16日 10:22:50   作者:apocelipes  
c++11 constexpr将变量声明为constexpr类型以便由编译器来验证变量是否是一个常量表达式,这篇文章主要给大家介绍了关于使用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://stackoverflow.com/questions/54258241/warning-iso-c-forbids-converting-a-string-constant-to-char-for-a-static-c

总结

到此这篇关于使用c++11 constexpr时遇到坑的文章就介绍到这了,更多相关c++11 constexpr坑内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • TypeScript的函数定义与使用案例教程

    TypeScript的函数定义与使用案例教程

    这篇文章主要介绍了TypeScript的函数定义与使用案例教程,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C语言驱动开发之判断自身是否加载成功详解

    C语言驱动开发之判断自身是否加载成功详解

    在驱动开发中我们有时需要得到驱动自身是否被加载成功的状态,这个功能看似没啥用实际上在某些特殊场景中还是需要的。本文将通过示例详细讲讲这一功能的实现方法,需要的可以参考下
    2022-10-10
  • c++查询最短路径示例

    c++查询最短路径示例

    这篇文章主要介绍了c++查询最短路径示例,需要的朋友可以参考下
    2014-05-05
  • OpenCV去除绿幕抠图源码

    OpenCV去除绿幕抠图源码

    这篇文章主要介绍了OpenCV去除绿幕抠图,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • 简单的socket编程入门示例

    简单的socket编程入门示例

    这篇文章主要介绍了简单的socket编程入门示例,简单实现client输入内容发送到server端输出,需要的朋友可以参考下
    2014-03-03
  • C语言实现简易扫雷游戏详解

    C语言实现简易扫雷游戏详解

    这篇文章主要为大家详细介绍了C语言实现简易扫雷游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • C++中的异常处理机制详解

    C++中的异常处理机制详解

    本文给大家分享的是C++中的异常处理机制。对如何处理异常、基本异常语法、异常保护代码等进行了探讨,推荐给大家。
    2017-04-04
  • 浅谈c++中“::”和“:” 冒号的意思

    浅谈c++中“::”和“:” 冒号的意思

    这篇文章主要介绍了浅谈c++中“::”和“:” 冒号的意思,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • VScode+cuda编程常见环境问题的解决

    VScode+cuda编程常见环境问题的解决

    本文主要介绍了VScode+cuda编程常见环境问题的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-02-02
  • C语言编一个数字益智小游戏

    C语言编一个数字益智小游戏

    这篇文章主要介绍了C语言编一个数字益智小游戏,本文通过实例截图的形式给大家展示的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-01-01

最新评论