【问题标题】:Unpacking a range of tuples into n-ary function将一系列元组解包成 n 元函数
【发布时间】:2019-02-01 17:46:13
【问题描述】:

假设我有一系列元组,例如来自 zip 功能。在该范围内运行的函数是否必须始终是一元的,或者是否存在一些将元组解包到函数参数中的转换。基本上,我想做以下事情:

  auto r1 = {1, 2, 3, 4};
  auto r2 = {'a', 'b', 'c', 'd'};
  auto chars = view::zip(r1, r2) | view::transform([](int a, char x) { return x; });

而不是显式使用 std::tie 或 std::apply。

【问题讨论】:

  • r1r2 不是元组。它们分别是std::initializer_list<int>std::initializer_list<char>
  • @NathanOliver 但我相信 view::zip(r1, r2) 是 std::tuple 的范围

标签: c++ range-v3


【解决方案1】:

听起来你真正需要的是一个爆炸元组参数的函数适配器。像这样的东西(LIVE):

#include <type_traits>
#include <utility>
#include <range/v3/core.hpp>
#include <range/v3/utility/semiregular.hpp>
#include <range/v3/utility/tuple_algorithm.hpp>

template<class F>
struct decomposed_fn
{
private:
    CONCEPT_ASSERT(ranges::CopyConstructible<F>());
    ranges::semiregular_t<F> f_;

    template<class FF>
    struct caller
    {
        FF &f_;

        template<class... Args>
        RANGES_CXX14_CONSTEXPR auto operator()(Args &&...args)
        RANGES_DECLTYPE_AUTO_RETURN_NOEXCEPT
        (
            ranges::invoke(f_, std::forward<Args>(args)...)
        )
    };

public:
    decomposed_fn() = default;
    RANGES_CXX14_CONSTEXPR explicit decomposed_fn(F f)
        noexcept(std::is_nothrow_move_constructible<F>::value)
    : f_(std::move(f))
    {}

    template<class T>
    RANGES_CXX14_CONSTEXPR auto operator()(T &&t)
    RANGES_DECLTYPE_AUTO_RETURN_NOEXCEPT
    (
        ranges::tuple_apply(caller<F>{f_}, std::forward<T>(t))
    )

    template<class T>
    RANGES_CXX14_CONSTEXPR auto operator()(T &&t) const
    RANGES_DECLTYPE_AUTO_RETURN_NOEXCEPT
    (
        ranges::tuple_apply(caller<F const>{f_}, std::forward<T>(t))
    )
};

template<class F,
    CONCEPT_REQUIRES_(ranges::CopyConstructible<std::decay_t<F>>())>
RANGES_CXX14_CONSTEXPR auto decomposed(F &&f)
RANGES_DECLTYPE_AUTO_RETURN_NOEXCEPT
(
    decomposed_fn<std::decay_t<F>>(std::forward<F>(f))
)

你可以用它来制定你的范围:

auto chars = view::zip(r1, r2)
    | view::transform(decomposed([](int, char x) { return x; }));

【讨论】:

  • 您介意解释一下额外的caller 助手类的目的吗?
  • @kyku caller 在那里是因为我想使用invoke 而不是让tuple_apply 直接调用适应的函数。我不认为这样做有什么好处,但它与 Ranges 设计的其余部分处理可调用对象的方式非常一致。
【解决方案2】:

不幸的是,似乎没有transform-apply 视图。像其他答案一样的简单解决方案是调整您的 lambda,以便使用 std::apply 调用它(相当于 std::bind_front(std::apply&lt;...&gt;, your_lambda)

// C++20
template<typename F>
constexpr auto apply_to(F&& f) noexcept(noexcept([f=static_cast<F&&>(f)]{})) {
    return [f=static_cast<F&&>(f)]<typename Tuple>(Tuple&& tuple) noexcept(noexcept(::std::apply(f, static_cast<Tuple&&>(tuple)))) -> decltype(auto) {
        return ::std::apply(f, static_cast<Tuple&&>(tuple));
    };
}

// C++17
// (Or C++14 with another std::apply implementation, like ranges::tuple_apply)
template<typename F>
constexpr auto apply_to(F&& f) {
    return [f=static_cast<F&&>(f)](auto&& tuple) noexcept(noexcept(::std::apply(f, static_cast<decltype(tuple)&&>(tuple)))) -> decltype(auto) {
        return ::std::apply(f, static_cast<decltype(tuple)&&>(tuple));
    };
}

只需将您的 lambda 包装为 apply_to([](int a, char x) { /*...*/ })


或者,结构化绑定非常短(在 C++17 中)

    // Be careful about excessive copying. Fine for the simple
    // `std::tuple<char, int>`, but consider forwarding references
    auto chars = view::zip(r1, r2) | view::transform([](auto zipped) {
        auto [a, x] = zipped;
        return x;
    });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-23
    • 2020-12-11
    • 2021-10-07
    • 2011-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多