Lambda 链的完美转发

Perfect Forwarding for Lambda Chaining

我想 chain/compose 多个 lambda 到一个新的 lambda。当使用参数调用新的 lambda 时,它应该使用这些参数调用原始的 lambda。这个 lambda 链接在刚刚复制参数时工作得很好,但是当我想修改它们时 完美转发 不起作用:

#include <boost/hana.hpp>
#include <cassert>

constexpr auto chain_lambdas = [](auto... lambdas) {
    constexpr auto lambdasTpl = boost::hana::make_basic_tuple(lambdas...);

    return [lambdas(lambdasTpl)](auto&&... args) {
        auto argsTpl = boost::hana::make_basic_tuple(std::forward<decltype(args)>(args)...);

        boost::hana::for_each(lambdas, [args(argsTpl)](auto lambda) mutable {
            boost::hana::unpack(std::move(args), [lambda(lambda)](auto&&... args) {
                lambda(std::forward<decltype(args)>(args)...);
            });
        });
    };
};


auto main() -> int
{
    auto lambda1 = [](bool& called){
        called = true;    
    };

    auto lambda2 =  [](bool& called){
        called = true;    
    };

    bool called = false;
    chain_lambdas(lambda1, lambda2)(called);

    assert(called == true);
    return 0;
}

compiler explorer 处的代码。完美转发是否适用于此用例?

auto argsTpl = boost::hana::make_basic_tuple(std::forward<decltype(args)>(args)...); 复制(移动)参数,因此您只需改变副本。

你需要 std::forward_as_tuple 等值的提升:

constexpr auto chain_lambdas = [](auto... lambdas) {
    constexpr auto lambdasTpl = boost::hana::make_basic_tuple(lambdas...);

    return [lambdas(lambdasTpl)](auto&&... args) {
        auto argsTpl = boost::hana::basic_tuple<decltype(args)...>(std::forward<decltype(args)>(args)...);

        boost::hana::for_each(lambdas, [args(argsTpl)](auto lambda) mutable {
            boost::hana::unpack(args, [lambda(lambda)](auto&&... args) {
                lambda(std::forward<decltype(args)>(args)...);
            });
        });
    };
};

Demo

在我这边,只有 std (C++17),你可能会这样做:

constexpr auto chain_lambdas = [](auto... lambdas) {
    return [=](auto&&... args) {
        return std::apply([&](auto... f){ (f(args...), ...); }, std::tie(lambdas...));
    };
};

Demo.

我为 args 删除了 std::forward,因为使用移动的对象调用函数可能不受欢迎,但如果需要,您可以启用它。