c++ tips4 cv修飾編

48
C++ TIPS 4 CV 修修修 Boost. 勉勉勉 #9 勉勉勉 ( 2012-05-26 )

Upload: -

Post on 30-Jun-2015

2.372 views

Category:

Technology


0 download

DESCRIPTION

主に cppll ML でご紹介してきた tips をC++の仕様をより掘り下げた形でまとめ直してみました。 今回はcv修飾にフォーカスした内容です。

TRANSCRIPT

Page 1: C++ tips4 cv修飾編

C++ TIPS 4 CV修飾編Boost. 勉強会 #9 つくば ( 2012-05-26 )

Page 2: C++ tips4 cv修飾編

概要 主に cppll ML でご紹介してきた tips

を C++ の仕様をより掘り下げた形でまとめ直してみました。

今回は cv 修飾にフォーカスした内容です。

C++ Tips Boost. 勉強会 #9 つくば 2

Page 3: C++ tips4 cv修飾編

概要 cv 修飾は C++ のすばらしい機能のひと

つではあるのですが、細部は残念ところが多々あり、注意が必要なのでしっかり復習しておきましょう。

C++ Tips Boost. 勉強会 #9 つくば 3

Page 4: C++ tips4 cv修飾編

CV修飾C++ Tips

Page 5: C++ tips4 cv修飾編

cv 修飾ってなに? const 修飾と volatile 修飾を合わせて

cv 修飾 ( cv-qualifiers ) と呼ばれます。 constexpr は cv 修飾とは関係ありません。

ヤツは 全然別物です。 cv 修飾は型とメンバー関数に対して付けるこ

とができます。 元の型とは違う型と見なされ、オーバーロード

の対象となります。C++ Tips Boost. 勉強会 #9 つくば 5

Page 6: C++ tips4 cv修飾編

const

const 修飾されたオブジェクトは JIS 的には定値オブジェクトと呼ばれます。

const は厳密には二段階の意味があります。 一つ目は読み込み専用 (readonly) であること。 二つ目は不変 (immutable) であること。

C++ Tips Boost. 勉強会 #9 つくば 6

Page 7: C++ tips4 cv修飾編

const

読み込み専用 (readonly) であること。 const なオブジェクトは書き換えられません。

C++ Tips Boost. 勉強会 #9 つくば 7

Page 8: C++ tips4 cv修飾編

const

不変 (immutable) であること。 生成時から const として作成されたオブジェ

クトは変更できません。 この場合、コンパイラはコンパイル時にオブ

ジェクトを作ってしまうことができます。また、そういうオブジェクトを const_cast などで const 外しを行い書き換えようとすると未定義動作となります。

C++ Tips Boost. 勉強会 #9 つくば 8

Page 9: C++ tips4 cv修飾編

const

const 参照あるいは const なオブジェクトを指し示すポインタは、その指し示すオブジェクトは readonly であるがimmutable であるとは限りません。

C++ Tips Boost. 勉強会 #9 つくば 9

Page 10: C++ tips4 cv修飾編

const

readonly だが immutable ではない例:

C++ Tips Boost. 勉強会 #9 つくば 10

int a = 0;const int & b = a;printf("%d\n", b); // 0a = 1;printf("%d\n", b); // 1b = 2; // NG

Page 11: C++ tips4 cv修飾編

volatile

volatile 修飾されたオブジェクトは JIS 的には揮発性オブジェクトと呼ばれます。

最適化の抑止を意味します。 マルチスレッドプログラミングにおける諸問

題の回避を行ってくれるわけではありません。 メモリマップド I/O などを想定した機能であ

り、実の所それ意外はほぼ要無しです。C++ Tips Boost. 勉強会 #9 つくば 11

Page 12: C++ tips4 cv修飾編

文法

C++ Tips Boost. 勉強会 #9 つくば 12

const int a = 0; // 前置int const b = 0; // 後置const volatile int e = 0; // const+volatilevolatile const int f = 0; // 順番は逆も OK で、意味は同じ。const int const c = 0; // 重複は NGconst int volatile d = 0; // 重複しない前置 + 後置は OKint * const g = 0; // ポインタに対する修飾は必ず後置int * const * h = 0; // ポインタに対する修飾は必ず後置const int * const * const i = 0; // ベース+各ポインタに対して

Page 13: C++ tips4 cv修飾編

文法

C++ Tips Boost. 勉強会 #9 つくば 13

const int a = 0; // a は変更不可const int * b = &a; // *b は変更不可 , b は変更可const int * const c = b; // c,*c は変更不可const int * const * const d = &c; // d,*d,**d は変更不可int e = 0; // e は変更可int * const f = &e; // f は変更不可 , *f は変更可int * const * const g = &e; // g,*g は変更不可 , **g は変更可int * const * h = &e; // *h は変更不可 , h,**h は変更可

Page 14: C++ tips4 cv修飾編

文法

C++ Tips Boost. 勉強会 #9 つくば 14

// 引数に対する cv 修飾に応じて// 適切にオーバーロードが機能します。void func(int & a);void func(int const & a);void func(int volatile & a);void func(int const volatile & a);void func(int && a);

Page 15: C++ tips4 cv修飾編

文法

C++ Tips Boost. 勉強会 #9 つくば 15

// 参照やポインタでない場合は関数シグネチャ的には無視される。void func(int a);void func(int const a); // 宣言的には void func(X a); と等価。

// 定義的には違いがある。void func(int a) { a = 0; // OK}void func(int const a) { a = 0; // const なので書き換えは NG}

Page 16: C++ tips4 cv修飾編

文法

C++ Tips Boost. 勉強会 #9 つくば 16

class X { public: void func(); void func() const; void func() volatile; void func() const volatile; void func() &&; // 呼び出し時の this に対する cv 修飾に応じて // 適切にオーバーロードが機能します。};

Page 17: C++ tips4 cv修飾編

文法

C++ Tips Boost. 勉強会 #9 つくば 17

class X { public: X(const int a) const; ~X() const; // コンストラクタとデストラクタに対する cv 修飾は NG // ( コンストラクタの引数型に対する cv 修飾は OK )

static void gunc() const; // 静的メンバー関数に対する cv 修飾は NG};

Page 18: C++ tips4 cv修飾編

「より cv 修飾されている」 cv 修飾に関する文脈上「より cv 修飾さ

れている (more cv-qualified) 」という表現が現れることがありますが、これは規格上次の表のように定義されています。

C++ Tips Boost. 勉強会 #9 つくば 18

Page 19: C++ tips4 cv修飾編

「より cv 修飾されている」

C++ Tips Boost. 勉強会 #9 つくば 19

修飾の順序関係

cv 修飾なし < const

cv 修飾なし < volatile

cv 修飾なし < const volatile

const < const volatile

volatile < const volatile

Page 20: C++ tips4 cv修飾編

const_cast

cv 修飾の状態のみを変換するキャストです。

名前は const_cast ですが、 volatile についても修飾を付けたり外したりできます。

C++ Tips Boost. 勉強会 #9 つくば 20

Page 21: C++ tips4 cv修飾編

const_cast

より cv 修飾されている型へは暗黙の型変換が行われる為、明示的にキャストする必要はありません。 これは安全なキャストについては暗黙の型変

換が行われるということであり、 const_cast が必要になるという事は安全でないキャストを行っているということであり、注意が必要です。

C++ Tips Boost. 勉強会 #9 つくば 21

Page 22: C++ tips4 cv修飾編

const_cast

C++ Tips Boost. 勉強会 #9 つくば 22

int a = 0;const int * b = &a;int * c = b; // NGint * d = const_cast<int *>(b); // const を一つ除去const int * * e = &d; // NG( 後述 )const int * * f = const_cast<const int * *>(&d); // const を一つ付加※const int * const * g = ...;int * * h = const_cast<int * *>(g); // const を一つ除去

Page 23: C++ tips4 cv修飾編

修飾の変換・互換ルール cv 修飾の差異による型の変換・互換ルール

は「より cv 修飾されている型へは暗黙の型変換が行われる」ということさえ頭に入れておけば後は概ね直感的に理解しやすいものなのですが、厳密にはやや変則的で落とし穴が無くもないのでひとつひとつ見ていきましょう!

C++ Tips Boost. 勉強会 #9 つくば 23

Page 24: C++ tips4 cv修飾編

型の互換性:値型

C++ Tips Boost. 勉強会 #9 つくば 24

int a = 0;const int b = a; // コピーint c = b; // コピー

... 値型の場合、 cv 修飾の状況の関わらずコピーされる。

※ サンプルは代入ですが、関数の引数についてもルールは同じ。

Page 25: C++ tips4 cv修飾編

型の互換性:参照型

C++ Tips Boost. 勉強会 #9 つくば 25

int a = 0;const int & b = a; // より cv 修飾されている型へは OKint & c = b; // 逆方向は NGint & d = const_cast<int &>(b); // キャストすればOK

Page 26: C++ tips4 cv修飾編

型の互換性:ポインター型

C++ Tips Boost. 勉強会 #9 つくば 26

int a = 0;const int * b = &a; // より cv 修飾されている型へは OKint * c = b; // 逆方向は NGint * d = const_cast<int *>(b); // キャストすればOKint * * e = &b; // NGint * * f = &d; // OKconst int * * g = &b; // OKconst int * * h = &d; // NG( 後述 )int * const * i = &b; // NGint * const * j = &d; // OKconst int * const * k = &b; // OKconst int * const * l = &d; // OK

Page 27: C++ tips4 cv修飾編

直感的でない型の非互換性 なぜ const int * * 型の値に int * * 型の値を代入できないのか?

C++ Tips Boost. 勉強会 #9 つくば 27

int a = 0;int * b = &a;const int * * c = &b; // NG

Page 28: C++ tips4 cv修飾編

直感的でない型の非互換性 ポインタ部分を除けば const int

型と int 型であり問題はない。

C++ Tips Boost. 勉強会 #9 つくば 28

int a = 0;const int b = a; // OK( コピー )

int c = b; // 逆方向も OK( コピー )

Page 29: C++ tips4 cv修飾編

直感的でない型の非互換性 ポインタがあっても一段の const int * 型と int * 型であれば同じく問題はない。

C++ Tips Boost. 勉強会 #9 つくば 29

int a = 0;int * b = &a;const int * c = b; // OKint * d = c; // NG

Page 30: C++ tips4 cv修飾編

直感的でない型の非互換性 しかし、 const int * * 型の値に int * * 型の値の代入はエラーになる。

C++ Tips Boost. 勉強会 #9 つくば 30

int a = 0;int * b = &a;const int * * c = &b; // NG

Page 31: C++ tips4 cv修飾編

直感的でない型の非互換性 もしエラーにならないとしたら?

C++ Tips Boost. 勉強会 #9 つくば 31

int a = 0;int * b = &a;const int * * c = &b; // 本当はエラー

Page 32: C++ tips4 cv修飾編

直感的でない型の非互換性 もしエラーにならないとしたら?

C++ Tips Boost. 勉強会 #9 つくば 32

int a = 0;int * b = &a;const int * * c = &b; // 本当はエラーconst int d = 0;*c = &d;*b = 1;

Page 33: C++ tips4 cv修飾編

直感的でない型の非互換性 もしエラーにならないとしたら?

C++ Tips Boost. 勉強会 #9 つくば 33

int a = 0;int * b = &a;const int * * c = &b; // 本当はエラーconst int d = 0;*c = &d; // *c も &d ともに const int * 型*b = 1;

Page 34: C++ tips4 cv修飾編

直感的でない型の非互換性 もしエラーにならないとしたら?

C++ Tips Boost. 勉強会 #9 つくば 34

int a = 0;int * b = &a;const int * * c = &b; // 本当はエラーconst int d = 0;*c = &d; // *c も &d ともに const int * 型*b = 1; // おかしくね? *b が指してんの d だよ!

Page 35: C++ tips4 cv修飾編

直感的でない型の非互換性 const int * * 型の値に int * *

型の値の代入を許してしまうと結果として明示的にキャストを使ってもいないのに const 外しを許してしまうことになってしまう! とまぁ、そんなことにならないように C++ の

コンパイラはエラーとして弾いてくれます。C++ Tips Boost. 勉強会 #9 つくば 35

Page 36: C++ tips4 cv修飾編

伝播性 ある変数を cv 修飾するとその変数を参照

あるいはポインタ経由でアクセスするコードが全て同じく cv 修飾されていることが要求されるようになります。

C++ Tips Boost. 勉強会 #9 つくば 36

Page 37: C++ tips4 cv修飾編

伝播性 オブジェクトが cv 修飾されると同様な効

果がそのデータメンバーとメンバー関数にも及びます。データメンバーについてはさらにそれらのデータメンバーのデータメンバーにも伝播が連鎖します。

C++ Tips Boost. 勉強会 #9 つくば 37

Page 38: C++ tips4 cv修飾編

伝播性 最終的に末端部分で呼び出す外部から提

供されるライブラリの関数 (API) が適切にcv 修飾されていない場合、必要に応じて const_cast を使用して cv 修飾を外すことになりますが、本当に外してもよいのか注意する必要があります。

C++ Tips Boost. 勉強会 #9 つくば 38

Page 39: C++ tips4 cv修飾編

mutable

オブジェクトが const 修飾されている場合でもデータメンバーに mutable を指定することで const 修飾を伝播させないこともできます。 const 修飾の伝播のみを無効化するもので、

mutable が指定されていても volatile 修飾は伝播します。

C++ Tips Boost. 勉強会 #9 つくば 39

Page 40: C++ tips4 cv修飾編

mutable

C++ Tips Boost. 勉強会 #9 つくば 40

class X { public: mutable int a; int b; X() { }};

const X x;x.a = 42; // mutable が効いているので代入可能x.b = 42; // const なので NG

Page 41: C++ tips4 cv修飾編

伝播性の抜け・・・

C++ Tips Boost. 勉強会 #9 つくば 41

const X x;//x.a = 42; これはさすがに NGprintf("%d\r\n", x.a); // 0*x.b = 1; // これはまだしも・・・printf("%d\r\n", x.a); // 1x.c = 2; // しれっと代入可能・・・printf("%d\r\n", x.a); // 2

class X { public: int a; int * b; int & c; X() :a(0), b(&a), c(a) { }};

Page 42: C++ tips4 cv修飾編

伝播性の抜け・・・ ほかにもコンストラクタおよびデストラク

タには cv 修飾がかからない為、コンストラクタおよびデストラクタ内では本来 *this が const な状態であっても cv 修飾がかかっていないものとして動作します。

const 修飾されているオブジェクトであっても delete で破棄できちゃったり・・・

C++ Tips Boost. 勉強会 #9 つくば 42

Page 43: C++ tips4 cv修飾編

伝播性の抜け・・・

C++ Tips Boost. 勉強会 #9 つくば 43

class X { public: int value; X() { value = 0; func(); } ~X() { value = 42; func(); } void func();};

const X x;

const X * y = new X();delete y;

Page 44: C++ tips4 cv修飾編

一時オブジェクトの延命 const且つ参照で受け取ることで一時オ

ブジェクトをその受け取った側の変数と同じスコープで延命できます。

C++ Tips Boost. 勉強会 #9 つくば 44

std::string func();

std::string name1 = func(); // コピーconst std::string name2 = func(); // コピーconst std::string & name3 = func(); // 一時オブジェクトの延命std::string & name4 = func(); // NG

Page 45: C++ tips4 cv修飾編

volatile メンバー関数でロック ちょっとトリッキーな volatile 修飾の使

い方として、スレッド間で共有して使われることがあるオブジェクトを volatile修飾によってマークし、そのメンバー関数が呼び出される際に volatile 修飾されている場合にのみロックを行う手法があります。

C++ Tips Boost. 勉強会 #9 つくば 45

Page 46: C++ tips4 cv修飾編

volatile メンバー関数でロック

C++ Tips Boost. 勉強会 #9 つくば 46

class X { public: int func() { ... } int func() volatile { return auto_lock(), const_cast<X *>(this)->func(); }};

X a;a.func(); // スレッド間共有しないインスタンスではロックせずに処理volatile X b;b.func(); // スレッド間共有するインスタンスではロックして処理

Page 47: C++ tips4 cv修飾編

質疑応答C++ Tips

Page 48: C++ tips4 cv修飾編

ご清聴ありがとうございました。C++ Tips