使用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坑內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- C++右值引用與移動構造函數基礎與應用詳解
- C語言char s[]和char* s的區別
- C語言的字符函數和字符串函數詳解
- C++示例講解string容器
- 淺談C++中const與constexpr的區別