♻️

C++ ラムダでPerfect Forwardキャプチャする方法メモ

2020/09/26に公開

ラムダの初期化キャプチャで Perfect Forward しようとして以下の様にしてしまうとバグる

template <class T>
auto get_printer(T &&val) {
    return [val = std::forward<T>(val)]() {
        std::cout << val << std::endl;
    };
}
int a = 10;
auto printer = get_printer(a);
printer(); // 10
a = 20;
printer(); // 20 じゃなくて 10 が表示されてしまう???

これは T が左辺値参照型の時に参照キャプチャじゃなくコピーキャプチャしてしまってるのが問題

なので T が左辺値参照型の時は reference_wrapper で、その他の時は move してキャプチャするみたいなことが必要になる

で、そういう事をちゃんとしようとすると非常にめんどくさそうだが tuple 使えばいい感じにできる

template <class T>
auto get_printer(T &&val) {
    return [val = std::tuple<T>(std::forward<T>(val))]() {
        std::cout << std::get<0>(val) << std::endl;
    };
}

これで左辺値参照の時でも左辺値参照のままキャプチャできるようなる

そして可変長テンプレートにも簡単に対応できる

template <class ...T>
auto get_printer(T &&...val) {
    return [val = std::tuple<T...>(std::forward<T>(val)...)]() {
        boost::fusion::for_each(val, [](auto &&val) {
            std::cout << val << std::endl;
        });
    };
}

https://boostjp.github.io/tips/tuple.html

もっと簡単な書き方あったら教えて


C++20 でラムダ式の初期化キャプチャでパック展開できるようになるらしいが結局 forward するときは使えないのでくそ

https://cpprefjp.github.io/lang/cpp20/allow_pack_expansion_in_lambda_init_capture.html

Discussion