リファレンスに対する add_const

以下のソースは正常にコンパイルすることができます。

BOOST_STATIC_ASSERT((is_same<add_const<int&>::type, int&>::value));

int& に const 修飾すると int& になるということです。
これは不思議で仕方がなかったので、調べてみました。


ポインタの場合はどうなるかというのを試してみること、

BOOST_STATIC_ASSERT((is_same<add_const<int*>::type, int*>::value));

これは当然のごとくコンパイルエラーです。正しくはこうなるはずです。

BOOST_STATIC_ASSERT((is_same<add_const<int*>::type, int* const>::value));

これなら成功です。int* に対する const 修飾はポインタに対して適用されるので、int* const になります。


ポインタが int* const になるということは、リファレンスは const int& ではなく int& const になるはずということは何となく想像が付くのですが、

BOOST_STATIC_ASSERT((is_same<add_const<int&>::type, int& const>::value));

これもコンパイルエラーです。ただし BOOST_STATIC_ASSERT に引っかかっているわけではなく、リファレンスに対して const 修飾を付けようとしているせいでエラーになっているようです。
まあできるわけが無いですよね。だからこそ自分は const int& になると思っていたわけで。


不思議だなー何でだろうなーと規格を漁ってると、こんな文面を見つけました。

Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef (7.1.3) or of a template type argument (14.4), in which case the cv-qualifiers are ignored.

N3035 §8.3.2¶1

cv 修飾されたリファレンスは cv 修飾が typedef やテンプレート型引数から導出される場合以外は ill-formed で、typedef やテンプレート型引数から導出される場合には cv 修飾は単に無視されます
なのでこんな風になります。

typedef int& const cref; // ill-formed

typedef int& ref;
typedef const ref cref2; // well-formed, int& const の cv 修飾が無視され、cref2 は int& になる

どうやら int& に対する const 修飾が int& になるのはこの仕様に基づくもののようです。
まあ int& に対する const 修飾なんて無意味ですし、ジェネリックに書こうとした場合でもテンプレート型引数での cv 修飾を無視してくれるのでありがたいことです。


次から C++ 新人がいたらこんなプログラムを見せて頭を捻って貰おうと思います。

typedef int& ref;
typedef const ref cref;

int n = 42;
cref cr = n;

cr = 893; // well-formed