C++を長く使っていると、やはりどうしてもよく使う機能と使わない機能がでてきます。 みなさんは、C++にこんな機能があったことを覚えているでしょうか? もしかしたら、すっかり忘れていた機能があるかもしれません。
explicitは、暗黙の型変換を防ぐためにコンストラクタにつける修飾子です。 具体例で見てみましょう。
001 class ArrayA { 002 public: 003 ArrayA(int size); 004 005 // ...実装など 006 }; 007 008 class ArrayB { 009 public: 010 explicit ArrayB(int size); 011 012 // ...実装など 013 }; 014 015 int main(void) { 016 ArrayA a(10); 017 ArrayB b(10); 018 019 // これが通ってしまう! 020 if(a == 100) { 021 // ... 022 } 023 024 // これは通らない 025 if(b == 100) { 026 // ... 027 } 028 029 return 0; 030 }
明らかに、20行目の条件式はおかしいですね。配列のArrayAクラスと、単なる数値は比較しようがありません。
にもかかわらず、これが通ってしまうのは、ひとえに「暗黙の型変換」のせいです。 20行目の条件式では、まずコンパイラはArrayAクラスとint型を引数に取るoperator==を探します。 しかしそんなものはどこにもないので、次にコンパイラは暗黙の型変換を探します。 すると、int型からArrayA型への型変換は、コンストラクタを利用して実行可能であることが分かり、 コンパイラはこの式を
020 if(a == ArrayA(100)) {
と解釈して、コンパイルを通してしまいます。
これを防ぐためのキーワードが、explicitです。 これが指定されたコンストラクタでは、それを使用しての暗黙の型変換が禁止されます。 std::vectorのコンストラクタなどに、実際の使用例があります。
mutableは、クラスのメンバ変数のみに指定可能な記憶クラス指定子です。 これを指定したメンバ変数は、constなメンバ関数内であっても変更可能になります。
実例を見てみましょう。
001 class Hoge { 002 private: 003 int value_; 004 mutable int mvalue_; 005 public: 006 void foo(void) const { 007 // これはダメ 008 // value_ = 0; 009 010 // これはOK 011 mvalue_ = 0; 012 } 013 };
mutable指定子は、const指定子やstatic指定子とは同時に指定できません。まあ、当然ですね。
あまり多用はしない方がいいとは思いますが、ここぞというときにはconst_castと並んで便利でしょう。
構造体やクラスに、例えばbool型のメンバ変数がたくさんあるときなどに、このビットフィールドは活躍します。 以下のような構文で使用します。
001 struct Hoge { 002 unsigned a:1; 003 bool b:1; 004 signed c:2; 005 unsigned d:4; 006 };
このように記述すると、変数aは符号なしの1ビット長整数、 変数bは1ビットのbool型変数、 変数cは符号つきの2ビット長整数、 変数dは符号なしの4ビット長整数になります。 コンパイラによっては、構造体のサイズを小さくできるかもしれません。
ちなみに、VCでは構造体のアライメントの関係でsizeof(Hoge)は1ではなく4になります。 あまり効果を実感できませんね……。
VCで効果を実感したければ、これくらいやる必要があります。
001 struct Hoge { 002 unsigned a:1; 003 unsigned b:1; 004 signed c:2; 005 unsigned d:4; 006 unsigned e:1; 007 unsigned f:1; 008 signed g:2; 009 unsigned h:4; 010 unsigned i:1; 011 unsigned j:1; 012 signed k:2; 013 unsigned l:4; 014 unsigned m:1; 015 unsigned n:1; 016 signed o:2; 017 unsigned p:4; 018 };
ここまでメンバ変数を増やしても、sizeof(Hoge)は4です。 これらの変数を仮に全部char型にするとsizeof(Hoge)は16になりますので、 これで効果を実感できるのではないかと思います。
ただし、このビットフィールドに対しては単項&演算子を使用できません。 つまり、ビットフィールドへのポインタというものは存在し得ないのです。 また、ビットフィールドへの参照は、const参照でない限り不可能です。
また、地味な注意点ですが、符号付きの1ビット長整数を宣言した場合、 ほとんどの処理系でその変数に代入できる値は0と-1であり、0と1ではない点に注意してください。 16ビット長や32ビット長の整数ではこんな細かいところは気にもかけませんが、 ビット長が短くなると整数の内部表現は結構重要な問題だったりします。 また他の注意点として、VCでは、bool型のビットフィールドは問答無用で1バイトになってしまうようです。
いろいろ調べてみたのですが、どうやらVCではビットフィールドはあまり使い物になりそうもありません。 VCでのビットフィールドの内部表現は、以下のようになっているみたいです。
分かり難いですが、bitfield用の領域は4バイト単位で確保され、 なおかつ余ったbitfield用領域にはpadding(詰め物)がされるだけで再利用もされないようなのです。 上のような構造体のsizeofをVCで調べると、なんと32というとんでもない大きさを吐いてくれます。 VCの構造体のアライメントについては、叩けばもっといろいろ出てきそうですが、今回はここら辺で。