の話。
C/C++ で、
a = f()/g();
のように書いた場合、f()
と g()
が実行される順序は規定されていない。
たぶん、コンパイラは実行速度を最適化するための方便のひとつとして、順序を入れ替える権利を持っていると思う。
で。上記の例では /
だったが、他の演算子の場合どうかというと。
|| と && (論理演算子)
a = f() && g();
この例だと、
-
f()
が実行される -
f()
の結果次第で、g()
が実行されたりされなかったりする
という順序になる。 ||
も同様。
ただし C++ の場合。
演算子の多重定義をしている場合は f()
と g()
のどちらを先に計算するのかが定義されない。この罠があるので、&&
と ||
の多重定義はお勧めできない。
, (コンマ演算子)
a =( f(), g() );
この例だと
-
f()
が実行される -
g()
が実行される
となる。式全体の値としては、g()
の値になる。
ただし C++ の場合。
演算子の多重定義をしている場合は f()
と g()
のどちらを先に計算するのかが定義されない。
この罠があるので、,
の多重定義はお勧めできないけど、 boost::assign
で多重定義されていてびっくりした。
なお。コンマ演算子は
something_t * p = get_something();
for( int i=0 ; i<10 && p ; ++i, p=p->next() ){
//略
}
とかで使われるコンマ。関数呼び出しのコンマはコンマ演算子ではないことに注意。
func( f(), (g(), h()), i() );
の(g(), h())
のコンマはコンマ演算子。それ以外のコンマは関数呼び出しのコンマ。
関数呼び出しのコンマで区切られた式の実行順序は規定されない。
上記の例だと、g()
より後に h()
が実行されることは間違いないけど、f()
や i()
が g()
より前になるのか、h()
より後になるのかは規定されない。コンパイラの気持ち次第。
? : (条件演算子)
a = f() ? g() : h();
この例だと、
-
f()
が実行される -
f()
の結果次第で、g()
またはh()
のいずれかが実行される
という順序になる。
ちなみに ? :
は多重定義できないので、罠はない。
それ以外全部の二項演算子
&&
、||
、,
以外の二項演算子は(たぶん)全部、演算の順序が規定されていない。
代入演算子も例外ではない。
コンパイラには、実行速度を上げるためにどちらを先に計算するのかを選ぶ自由がある。と思う。
tweet されていた例
というわけで、
std::map<int,size_t> m;
m[123]=m.size();
と書いた場合、m[123]
と m.size()
の実行順序は規定されていない。
m[123]
は m
の大きさを変えるという副作用を伴う。
m[123]
が先に実行されると m.size()
は 1
になり、m.size()
が先なら m.size()
は 0
になる。
要するに、これはよくない計算である。
一方。
ciel さんの提案した
std::map<int,size_t> m;
m.insert(std::make_pair(123, m.size()));
C++ は遅延評価しないので、引数の評価が終わるまで関数が呼べない。
したがって、
-
m.size()
が実行される - `std::make_pir(略) が実行される
- m.insert(略) が実行される
という順序で実行される。曖昧さがなく、安全。