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