Download - C++ Tips 3 カンマ演算子編
C++ TIPS 3 カンマ演算子編Boost. 勉強会 #8 大阪 ( 2012-02-11 )
概要 主に cppll ML でご紹介してきた tips を C++ の仕様をより掘り下げた形でまとめ直してみました。 今回はカンマ演算子にフォーカスした内容です。
C++ Tips Boost. 勉強会 #8 大阪 2
カンマ演算子C++ Tips
カンマ演算子ってなに? セミコロン ( ; ) が文の区切りとして使えるように、カンマ ( , ) が文節として使えます。
日本語文章で例えると「;」が「 。 」に対応するのに対して「,」は「、」に対応しています。 この用法で使われるカンマはカンマ演算子と呼ばれます。
C++ Tips Boost. 勉強会 #8 大阪 4
カンマ演算子ってなに? 関数呼び出しのカンマや初期化ブロック内のカンマ区切りはカンマ演算子ではありません。 関数呼び出しのカンマ区切りと違って評価順は左側の項目が先と決まってます。 カンマ演算子はもっとも優先順位の低い演算子となります。
C++ Tips Boost. 勉強会 #8 大阪 5
カンマ演算子の動き
C++ Tips Boost. 勉強会 #8 大阪 6
int a() { puts("a"); return 1; }int b() { puts("b"); return 2; }int c() { puts("c"); return 4; }void func() { int x = (a(),b(),c()); printf("%d\n",x);}
■ 実行結果abc4
左から順に評価され最も右側の値が式の評価値となります。( オーバーロードされていない場合 )
余談というか落とし穴
C++ Tips Boost. 勉強会 #8 大阪 7
int x = (a(),b(),c());… のつもりで…int x = a(),b(),c();… と書くとカンマ演算子ではなく、宣言のカンマ区切りだとコンパイラに解釈され…int x = a(); // a() を実行して x に初期値にする。int b(); // 戻り型が int の関数 b() の宣言。int c(); // 戻り型が int の関数 c() の宣言。… の意味になってしまいます。
使用例: if
C++ Tips Boost. 勉強会 #8 大阪 8
if (a(),b(),c()) // c() 結果で分岐{ ... }elseif (d(),e(),f()) // f() 結果で分岐{ ... }elseif (g(),h(),i()) // i() 結果で分岐{ ... }else{ ... }
使用例: if
C++ Tips Boost. 勉強会 #8 大阪 9
if (x=a(),b(x),c()) // c() の結果で分岐{ ... }elseif (d(),e=x+i,f(e)) // f(e) の結果で分岐{ ... }elseif (g(),i=h(),i++) // i++ の結果で分岐{ ... }else{ ... }
使用例: switch
C++ Tips Boost. 勉強会 #8 大阪 10
if (a(),b(),c()) // c() の結果で分岐{ ... }elseswitch(d(),e(),f()) // f() の結果で分岐{case 0: ...default: ...}
使用例: while,do/while
C++ Tips Boost. 勉強会 #8 大阪 11
while(a(),b(),c()) // c() の結果で分岐{ ... }
do{ ... }while(d(),e(),f()); // f() の結果で分岐
使用例: for
C++ Tips Boost. 勉強会 #8 大阪 12
for(int i=0,j=0; a(i),b(j); c(&i),d(&j)){ ... } // b() の結果で分岐
※ 最初のカンマはカンマ演算子ではなく宣言のカンマ区切りとなります。
使用例:引数
C++ Tips Boost. 勉強会 #8 大阪 13
x((a(),b(),c()), (d(),e(),f()), (g(),h(),i()));
この場合、( )各グループ内では… a()→b()→c()、d()→e()→f()、 g()→h()→i() …の順で実行されますが、abc、def、ghiの各グループの実行される順番は処理系依存となる為、処理系によって abc→def→ghi だったり ghi→def→abc だったりします。 この順番は通常、関数の引数がどのような順番でスタックに積
まれるか依存します。
使用例: || と && 組み合わせて
C++ Tips Boost. 勉強会 #8 大阪 14
( (a(),b(),c()) || (d(),e(),f()) // c() が false の場合に実行) && (g(),h(),i()); // c()||f() が true の場合に実行
\ ヒャッハー! /
C++ Tips Boost. 勉強会 #8 大阪 15
int x = ( (++i,y=a(),b(y),c(i)) || (++i,y=d(),e(i),z=f(y,i) ? g(): h())) && ( (++i,y=a(),b(y),c(i)) || (++i,y=d(),e(i),y<=f(y,i)));
スタック領域の圧迫について
C++ Tips Boost. 勉強会 #8 大阪 16
カンマ演算子を多用して頑張ると一文で結構な量のコードが書けてしまうのですが、あんまり調子扱いてると一時変数の量が膨大になってスタック領域を圧迫し状況によってはスタックオーバーフローを招くことにもなりかねない観点からもほどほどに。
インラインロック カンマ演算子は左側の項目から評価されるという事と、一時オブジェクトの寿命は文の終了時までという事を利用し、コンストラクタでロック、デスクトラクタでアンロックを行うクラスを用意しておけばインラインでの手軽なロック / アンロックができます。
C++ Tips Boost. 勉強会 #8 大阪 17
インラインロック
C++ Tips Boost. 勉強会 #8 大阪 18
auto_lock(),func();
■ 実行される順番1.auto_lock::auto_lock()2.func()3.auto_lock::~auto_lock()
インラインロック
C++ Tips Boost. 勉強会 #8 大阪 19
if (auto_lock(),func()) { func2(); }
■ 実行される順番1.auto_lock::auto_lock()2.func()3.auto_lock::~auto_lock()4.func2() →func() が true の場合にのみ実行される。
オーバーロード カンマ演算子はオーバーロードしてその挙動をユーザー定義することも可能です。
C++ Tips Boost. 勉強会 #8 大阪 20
inline hoge operator,(hoge a, hoge b) { return a; // b の代わりに a を返す。}
初期化リスト代わり カンマ演算子のオーバーロードを頑張れば C++11 の初期化リストの代わりになるような類いのもの実装可能です。
C++ Tips Boost. 勉強会 #8 大阪 21
オーバーロード 注意点 その他演算子オーバーロード違い、カンマ演算子のオーバーロードは見た目からはオーバーロードされていることが予測し辛い為、容易くメンテナンス性の悪いコードになってしまいますので、乱用は厳禁です。 C++03 時代であればまだ初期化リスト代わりの用途としてカンマ演算子のオーバーロードが有効なシーンもあったものの C++11 では素直に初期化リストを使ったほうがいいです。 さらに C++03 でも昔のコンパイラではカンマ演算子のオーバーロードまわりはコンパイラの挙動がバギーで使い物にならなかったり・・・
C++ Tips Boost. 勉強会 #8 大阪 22
質疑応答C++ Tips
ご清聴ありがとうございました。C++ Tips