C++の修飾子(qualifier)には,const
修飾子とvolatile
修飾子の2種類がある.
この2つをあわせて“cv修飾子”(cv-qualifier)と呼んでいる.
よくある間違いに,unsigned
も修飾子だと思っているケースがあるけど,
正しくはunsigned
は“型名の一部”であって修飾子ではない.
この違いはtypedef
で新しい型名をつくってみると明確になる.
たとえば,typedef int int32;
として,新しく“int32
”という型をつくったとする.
const
は修飾子なので任意の型を修飾でき,
f(const int32 n)
のように記述できる(サンプルプログラム1).
これに対してunsigned
は修飾子ではないので
f(unsigned int32 n)
と書くとコンパイルエラーになる(サンプルプログラム2).
サンプルプログラム1: OK | サンプルプログラム2: NG |
---|---|
typedef int int32; void f(const int32 n) { printf("%u\n", n); } |
typedef int int32; void f(unsigned int32 n) { printf("%u\n", n); } |
完全な型名 | 省略形 |
---|---|
signed char |
char |
signed short int |
short ,short int |
signed int |
int |
signed long int |
long ,long int |
unsigned char |
|
unsigned short int |
unsigned short |
unsigned int |
unsigned |
unsigned long int |
unsigned long |
こういう誤解がおこるのは,unsigned
というキーワードが,const
修飾子と同じように
int
などの型名の前に書かれるという構文上の特徴があるからだと思うのだけど,
int
が正確にはsigned int
だということを思い出せば,
signed int
をunsigned
で修飾してunsigned signed int
になってはヘンだとわかるだろう.
また,変数定義などの際に,単にunsigned
とかくとunsigned int
の意味に解釈されるのも
unsigned
が単なる修飾子だとするとヘンな感じだ
(これがOKなら単にconst
と書いたらconst int
の意味になるというルールもあるはずだし).
また,const
やvolatile
が,変更できないとか揮発性であるというような,
どんな型にも共通の性質を追加しようとするものであるのに対して
unsigned
(符号なし)は少なくとも数値を格納する型にしかつけられそうにない点も明確な差がある.
[これについては,unsigned
は整数型にしかつけられませんという制限のある修飾子なんだと無理やり解釈する手が残ってるけども.]
いずれにしても,よく考えてみると書き方は似てるけど違うものっぽいとは思うはず.
char* s
にconst
修飾子をつける場合,
const
が書ける位置は3つある.
# | バリエーション | s の変更 |
*s の変更 |
解釈 |
---|---|---|---|---|
1 | f1(const char * s); |
○ | × | const char へのポインタと解釈 |
2 | f2(char const * s); |
○ | × | 同上(const char* と解釈) |
3 | f3(char * const s); |
× | ○ | 変数s が定数(const )と解釈 |
サンプルプログラム4 |
---|
0001 typedef char* string; 0002 0003 const string f(const string s) { 0004 *s = 'H'; 0005 s += 7; 0006 return s; 0007 } |
ここまでは わりと知られている話…
問題は次で,たとえば“typedef char* string;
”として新しい型string
をつくっておき,
これにconst
をつけて,“const string s
”とすると
“const char * s
”と解釈されるのか“char * const s
”と解釈されるのか,どっち?という問題.
前者ならサンプルプログラム4の4行目がエラーになり,後者なら5行目がエラーになる.
この問題,単純に
“const string s
”→“const char * s
”なので前者が正解と思ったら大間違い.
実際には後者(char * const s
)と解釈されるのが正解.
なぜこうなるのかというと…
const
と型名はどちらを先に書いてもよい.
“const int a = 5;
”と“int const a = 5;
”は同じ意味と解釈される
[サンプルプログラム5a/5b].
これは表1の#2で“f(char const * s);
”が“f(const char * s);
”
と解釈されていることからもわかる.
typedef
は新しい型をつくる.
したがって,“typedef char* string;
”によってつくられたstring
はint
などと同じように独立したひとつの型になる
(char*
はひとかたまりなのであって,もはやchar
と*
に分解されることはない),
const string s
”は
“string const s
”→“char* const s
”と解釈される.
というのが正解.
サンプルプログラム3 | 表1 const の位置と解釈 |
サンプルプログラム4 | const string s はconst char * s とchar * const s のどっち? |
サンプルプログラム5a/5b | const int はint const と書いてもよい |