gpt4 book ai didi

c++ - 调用元组元素的公共(public)方法

转载 作者:行者123 更新时间:2023-12-02 01:33:37 24 4
gpt4 key购买 nike

假设我有一个类型为 T1,...,TN 的元组,它实现了某种方法 apply()

如何定义一个函数,该函数采用此元组和一些初始元素,并返回对此元素的 apply() 的链式调用?

例如:

template <typename... Args, typename Input>
auto apply(std::tuple<Args...> const &tpl, Input x) {
// return ???
}

// simple example

struct Sqr {
static int apply(int x) { return x * x; }
};

enum class Choice {
One,
Two,
};

struct Choose {
static int apply(Choice choice) {
switch (choice) {
case Choice::One:
return 1;
case Choice::Two:
return 2;
}
}
};

void test() {
auto tpl = std::tuple(Sqr{}, Choose{});
assert(apply(tpl, Choice::One) == 1);
assert(apply(tpl, Choice::Two) == 4);
}

我尝试使用 fold expressions ,以及以下答案的变体:Template tuple - calling a function on each element但无法编译任何内容。

主要区别在于我需要每次调用的结果作为下一次调用的输入。

具体来说,我尝试了以下方法,但失败了,因为它使用初始值调用每个参数:

template <typename... Args, typename Input>
auto apply(std::tuple<Args...> const &tpl, Input x) {
return std::apply([&x](auto &&... args) {
return (..., args.apply(x));
}, tpl);
}

澄清和假设:

  • 我希望以特定顺序调用这些方法 - 从最后到第 - 类似于 mathematical function composition
    (f * g)(x) := f(g(x))
  • 每个元组参数的输入和输出类型不受限制。唯一的假设是连续的参数在相应的类型上一致。

最佳答案

可能有更时髦的 C++17 方法可以做到这一点,但总有好的老式部分专用递归。我们将制作一个struct代表你的递归算法,然后我们将围绕它构建一个函数包装器 struct帮助类型推断。首先,我们需要一些导入。

#include <tuple>
#include <utility>
#include <iostream> // Just for debugging later :)

这是我们的结构定义。

template <typename Input, typename... Ts>
struct ApplyOp;

不太有趣。这是一个不完整的类型,但我们将提供特化。与任何递归一样,我们需要一个基本情况和一个递归步骤。我们正在对元组元素进行归纳(您可以将其视为类似折叠的操作,这是正确的),因此我们的基本情况是元组为空时。

template <typename Input>
struct ApplyOp<Input> {
Input apply(Input x) {
return x;
}
};

在这种情况下,我们只需返回 x 。计算完成。

现在我们的递归步骤采用可变数量的参数(至少一个)并调用 .apply .

template <typename Input, typename T, typename... Ts>
struct ApplyOp<Input, T, Ts...> {
auto apply(Input x, const T& first, const Ts&... rest) {
auto tail_op = ApplyOp<Input, Ts...>();
return first.apply(tail_op.apply(x, rest...));
}
};

tail_op是我们的递归调用。它实例化 ApplyOp下一个版本。有两个apply在此代码中调用。 first.applyapply类型 T 中的方法;这是您控制的方法,它确定每个步骤会发生什么。 tail_op.apply是我们对 this 的另一个版本的递归调用 apply函数或基本情况,取决于什么Ts...是。

请注意,我们还没有谈到任何有关元组的内容。我们刚刚获取了一个可变参数包。我们将使用 std::integer_sequence 将元组转换为参数包。 (更具体地说, std::index_sequence )。基本上,我们想要一个包含 N 的元组。元素并将其转换为表单的参数序列

std::get<0>(tup), std::get<1>(tup), ..., std::get<N-1>(tup)

所以我们需要从 0 获取索引序列高达N-1包含(其中 N-1 是我们的 std::tuple_size )。

template <typename Input, typename... Ts>
auto apply(const std::tuple<Ts...>& tpl, Input x) {
using seq = std::make_index_sequence<std::tuple_size<std::tuple<Ts...>>::value>;
// ???
}

那个看起来复杂的类型别名正在构建我们的索引序列。我们获取元组的大小 ( std::tuple_size<std::tuple<Ts...>>::value ) 并将其传递给 std::make_index_sequence ,这给了我们一个 std::index_sequence<0, 1, 2, ..., N-1> 。现在我们需要将该索引序列作为参数包获取。我们可以通过一层额外的间接来实现类型推断。

template <typename Input, typename... Ts, std::size_t... Is>
auto apply(const std::tuple<Ts...>& tpl, Input x, std::index_sequence<Is...>) {
auto op = ApplyOp<Input, Ts...>();
return op.apply(x, std::get<Is>(tpl)...);
}

template <typename Input, typename... Ts>
auto apply(const std::tuple<Ts...>& tpl, Input x) {
using seq = std::make_index_sequence<std::tuple_size<std::tuple<Ts...>>::value>;
return apply(tpl, x, seq());
}

第二个apply是外部用户调用的电话。他们传递一个元组和一个输入值。然后我们构造一个 std::index_sequence适当类型并将其传递给第一个 apply ,它使用该索引序列依次访问元组的每个元素。

Complete, runnable example

关于c++ - 调用元组元素的公共(public)方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72871304/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com