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と書いてもよい |