ラムダよくわからない
ラムダの挙動は難しいです。
コピーキャプチャされた要素の型に関してはdecltypeを用いた具体例による説明が規格内にありました。
void f3() { float x, &r = x; // x and r are not captured (appearance in a decltype operand is not an odr-use) [=] { decltype(x) y1; // y1 has type float decltype((x)) y2 = y1; // y2 has type float const& because this lambda is not mutable and x is an lvalue decltype(r) r1 = y1; // r1 has type float& (transformation not considered) decltype((r)) r2 = y2; // r2 has type float const& }; }
以下のコードの挙動はgcc, clangで共通しています。
しかし、staticがコピーキャプチャ且つmutableな場合に内部的にxのコピーが生成されずに外部のstaticなxが使われているのは、規格のどの部分で裏付けされているのか未だによくわかりません。
個人的にはコピーキャプチャしている時点で、外部のxに副作用が及ばない事を期待したコードなのですが。
Lambda expressionsの項内には見当たらなかった気がするのでOne definition rule辺りでしょうか。
#include <type_traits> #include <cassert> int main() { { int x = 0; [&]() { assert(!std::is_reference<decltype(x)>::value); // 参照キャプチャしても型は非参照型 ++x; // 参照キャプチャ(xは非参照型)への変更 assert(x == 1); }(); assert(x == 1); // キャプチャされたxは非参照型だったが、外部のxに変更は及ぶ } { int tmp = 0; int& x = tmp; [=]() mutable { assert(std::is_reference<decltype(x)>::value); // コピーキャプチャしても型は参照型 ++x; // コピーキャプチャ(xは参照型)に対する変更 assert(x == 1); }(); assert(x == 0); // 外部のxに変更は及ばない } { static int x = 0; [=]() mutable { assert(!std::is_reference<decltype(x)>::value); // staticなint型をコピーキャプチャしても当然参照型ではない ++x; // 外部のxに変更が及ばない事を期待したはずのコピーキャプチャに対する変更 assert(x == 1); }(); assert(x == 1); // 外部のxに変更が及ぶ(外側のstaticなxがそのまま使われた(?)) } }
参照キャプチャは非参照型のオブジェクトに対する副作用がスコープ外部まで伝わったりする魔法です。
この挙動は実際、コンパイラがよきにはからってくれているとしか言えません。
ラムダの挙動は難しいです。
規格やコンパイラの正しさもよくわからなくなってきます。