auto類型推導(dǎo)規(guī)則
C++11中新增了使用auto進(jìn)行自動(dòng)類型推斷的功能,從此使用容器等復(fù)雜類型時(shí)涵妥,可以簡(jiǎn)化代碼乖菱,非常方便。
但一開始使用auto時(shí)蓬网,有時(shí)候會(huì)看到這樣的代碼:
int x = 0;
const auto *y = &x;
這十分讓人迷惑,auto不是可以自動(dòng)類型推導(dǎo)嗎皮官?為什么有時(shí)使用auto還要加上const和星號(hào)讯沈,有時(shí)候又不需要?auto所代表的類型到底包不包括const冰木?
我們用代碼實(shí)驗(yàn)一下逼龟,實(shí)驗(yàn)方法是通過IDE(我用的是CLion)的debug模式匀钧,打斷點(diǎn)查看變量的類型。
這里分普通變量、引用、指針,三種情況分別討論。
-
對(duì)于普通變量,auto的推導(dǎo)結(jié)果會(huì)忽略const修飾符。簡(jiǎn)單實(shí)驗(yàn)
const int x = 0; auto y = x; // y -> int 沒有保留const const auto z = x; // z -> const int 額外增加了const
在調(diào)試模式下查看變量類型:
可以看到y(tǒng)的類型是int骤素,忽略了const修飾。
-
對(duì)于引用,auto的推導(dǎo)結(jié)果會(huì)保留const修飾符浴讯。
const int x = 0; auto &y = x; // y的類型是 const int & 保留了const int a = 0; auto &b = a; // b -> int & const auto &c = a; // c -> const int &
可以看到奈籽,雖然a沒有const修飾狼忱,但可以在變量c定義時(shí)額外增加const修飾饲帅。
-
對(duì)于指針丘逸,我們舉一個(gè)最簡(jiǎn)單的例子:
int *a = 0; const int *b = 0; auto x = a; // x -> int* auto y = b; // y -> const int*
可以看到湃鹊,b的const修飾也保留到了y上余赢。
經(jīng)過上面三種情況的討論举塔,我們得到了結(jié)論:
用auto初始化的變量,普通變量的const修飾會(huì)忽略聊替,而指針和引用的const修飾會(huì)保留当纱。
指針的const修飾
但是真的這么簡(jiǎn)單嗎晨横?我們知道手形,指針可以有兩個(gè)const修飾,例如 const int* p
和 int *const p
代表不同的含義:
如果p的類型是 const int*
顷帖,那么無法通過p來修改q的內(nèi)容陶舞,也就是12這個(gè)值舅巷。例如: *p = 7
不合法钠右,因?yàn)闊o法給*p賦值。
而如果p的類型是 int *const
忘蟹,那么無法修改p的內(nèi)容飒房,也就是p只能指向0x01E0搁凸,不能指向其他地址。
當(dāng)然情屹,結(jié)合二者坪仇,如果p的類型是 const int *const
則圖中p和*p的內(nèi)容都不可修改。
可能一開始分不清這兩個(gè)const垃你,也很容易記混。實(shí)際上喂很,const優(yōu)先修飾其左邊相鄰的類型惜颇,如果左邊沒有類型,則修飾其右邊相鄰的類型少辣。所以 const int*
等價(jià)于 int const*
(const修飾的都是int)凌摄,卻不等于 int *const
(const修飾的是int*)。
const修飾誰漓帅,誰就不能修改锨亏。 const int*
中的const修飾int,代表int的值也就是q的值不能修改忙干;而 int * const
的const修飾的是 int*
器予,代表int*的值也就是p的值不能修改。
深究auto與指針的const
回到上面的auto推導(dǎo)指針類型的討論捐迫,你是否注意到乾翔,保留下來的是哪一個(gè)const?回到上面看一眼施戴,是左邊的const反浓。那么右邊的const能否保留呢?我們用下面的代碼實(shí)驗(yàn)一下:
int *a = 0;
const int *b = 0;
int *const c = 0;
const int *const d = 0;
auto m = a; // m -> int*
auto n = b; // n -> const int* 保留了左邊的const
auto p = c; // p -> int* 忽略了右邊的const
auto q = d; // q -> const int* 保留左邊赞哗,忽略右邊的const
const auto r = a; // r -> int* const 額外增加右邊的const
可以看到雷则,右邊的const是不會(huì)保留的。但定義變量r的時(shí)候肪笋,我們?cè)赼uto前增加const月劈,成了右邊的const。你可能要問了涂乌,為什么我明明在auto左邊加的const艺栈,結(jié)果沒有變成const int* ,卻跑到了右邊變成了int* const?
回顧一下前面說的湾盒,const優(yōu)先修飾左邊相鄰的類型湿右,如果沒有,則修飾其右邊相鄰類型罚勾。在 const auto r = a;
中毅人,const修飾的是auto吭狡,而auto推導(dǎo)為int,所以const修飾的是int丈莺,也就成了 int* const
划煮。
那么,又沒有辦法缔俄,把const加在左邊呢弛秋?
辦法是有的,我們可以這樣寫:
const auto * r = a; // r -> const int*
// 等價(jià)于下面的定義:
auto const * r = a; // r -> const int* 俐载,也可以寫作 int const*
我們?cè)赼uto后面加一個(gè)星號(hào)蟹略,這樣一來,auto推導(dǎo)為int遏佣,const修飾auto挖炬,也就是修飾int,而不是修飾int状婶,就成了 const int*
了意敛。const和auto的位置換一下可以更清楚得看到,相當(dāng)于我們把 * 從auto里面拆了出來膛虫,把const放在了二者中間草姻。
只需要記住,const如果在最左邊走敌,那么它修飾的是右邊緊鄰的類型碴倾。對(duì)于 const int*
,const修飾的是int掉丽,而不包括星號(hào)跌榔。
auto推導(dǎo)與const修飾——一個(gè)原則
回到最初的問題,auto所代表的類型到底包不包括const捶障?前面分了三種情況討論僧须,其實(shí)總結(jié)下來,就是一個(gè)原則:
auto類型推導(dǎo)會(huì)在語法正確的前提下项炼,盡量少包含const担平。
對(duì)于引用,下面的語句中锭部,b的初始化是不合法的:
const int a = 0;
int &b = a; // 不合法
const int &c = a; // 合法
不能用一個(gè) int& 類型引用一個(gè)const int類型的變量暂论,因此,當(dāng)使用auto自動(dòng)推導(dǎo)時(shí)拌禾,也必須帶上const取胎;
對(duì)于指針,也是類似:
const int a = 0;
const int* const b = 0;
int* m = &a; // 不合法
int* n = b; // 不合法
const int* p = b; // 合法,左邊的const不能丟闻蛀,右邊的可以丟
指針給指針賦值時(shí)匪傍,例如變量b包含兩個(gè)const,左邊的const必須保留觉痛,右邊的則可以忽略役衡。因此,使用auto進(jìn)行類型推導(dǎo)時(shí)薪棒,也會(huì)保留左邊的const手蝎。
最初的問題
回到最初的問題,在第一個(gè)例子中俐芯,為什么要使用 const auto *y = &x
這樣的方式來定義y的類型柑船?
這是因?yàn)閤本身不是常量,沒有const修飾泼各。只有用這種方式,才能得到一個(gè)常量指針亏拉,保證不會(huì)通過y來修改x的內(nèi)容扣蜻。相信通過上面的分析和實(shí)驗(yàn),你可以理解了及塘。
總結(jié)
這篇文章的知識(shí)點(diǎn):
- const修飾與它緊鄰的類型莽使,優(yōu)先修飾其左邊的類型。
- auto在類型自動(dòng)推導(dǎo)時(shí)笙僚,會(huì)在語法正確的前提下盡量少添加const芳肌。
- 使用
const auto *
的方式,我們可以初始化一個(gè)常量指針肋层。