调用元组元素的常见方法
假设我有一个类型的元组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表达式,以及答案的变化: 模板元素 - 在每个元素上调用一个函数,但无法获得任何要编译的东西。
主要区别是我需要每个调用的结果作为下一个调用的输入。
具体而言,我尝试了以下操作,它失败了,因为它调用了每个参数的初始值:
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);
}
澄清和假设:
- 我希望以特定顺序调用这些方法 - 最后一个 - 与
- 每个元组参数的输入和输出类型未限制。唯一的假设是连续参数就相应类型一致。
Say I have a tuple of types T1,...,TN
that implement some method, apply()
.
How do I define a function that takes this tuple and some initial element, and returns the chained call of apply()
on this element?
For example:
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);
}
I tried to use fold expressions, and variations of answers from: Template tuple - calling a function on each element but couldn't get anything to compile.
The main difference is that I need each invocation's result as the input for the next one.
Concretely, I tried the following, which failed because it calls each argument with the initial value:
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);
}
Clarifications and assumptions:
- I want the methods to be called in a specific order - last to first - similarly to mathematical function composition.
(f * g)(x) := f(g(x))
- The input and output types of each tuple argument are not constricted. The only assumption is that consecutive arguments agree on the corresponding types.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
C ++可能会有17种方法,但总有良好的老式部分特权递归。我们将制作一个代表您的递归算法的
struct
,然后我们将围绕该struct
构建功能包装器,以帮助类型推理。首先,我们需要一些进口。这是我们的结构定义。
不是很有趣。这是一种不完整的类型,但我们将提供专业化。与任何递归一样,我们需要一个基本情况和递归步骤。我们正在将元组元素引入(您可以将其视为类似折叠的操作),因此我们的基本情况是元组为空时。
在这种情况下,我们只返回
x
。计算完成。现在,我们的递归步骤采用了可变数量的参数(至少一个),并调用
.apply
。tail_op
是我们的递归调用。它实例化 版本的applyop
。此代码中有两个应用
调用。first.apply
是在类型
方法;这是您控制的方法来确定每个步骤发生的情况。t
中应用tail_op.apply
是我们对 this的另一个版本的递归调用...
是。请注意,我们尚未对元素说任何话。我们刚刚采用了variadic参数包。我们将使用
std :: integer_sequence
(更具体地说,是std :: index_sequence
)。基本上,我们要服用一个包含n
元素的元组,然后将其转换为表单的一系列参数,因此我们需要从
0
到n-1 包容性(其中
n-1
是我们的std :: tuple_size
)。那种复杂的类型别名正在构建我们的索引序列。我们采用元组的尺寸(
std :: Tuple_size&lt; std :: Tuple&lt; ts ...&gt;&gt; :: value
)并将其传递给std :: make_index_sequence
,这为我们提供了std :: index_sequence&lt; 0,1,2,...,n-1&gt;
。现在,我们需要将该索引序列作为参数包。我们可以使用额外的间接层进行类型推理来做到这一点。第二个
应用
是外部用户打电话的一个。他们通过元组和输入值。然后,我们构建适当类型的std :: index_sequence
,并将其传递给第一个apply
,该使用该索引序列依次访问元组的每个元素。完成,可运行的示例
There may be snazzier C++17 ways of doing it, but there is always good old-fashioned partially-specialized recursion. We'll make a
struct
that represents your recursive algorithm, and then we'll build a function wrapper around thatstruct
to aid in type inference. First, we'll need some imports.Here's our structure definition.
Not very interesting. It's an incomplete type, but we're going to provide specializations. As with any recursion, we need a base case and a recursive step. We're inducting on the tuple elements (you're right to think of this as a fold-like operation), so our base case is when the tuple is empty.
In this case, we just return
x
. Computation complete.Now our recursive step takes a variable number of arguments (at least one) and invokes
.apply
.The
tail_op
is our recursive call. It instantiates the next version ofApplyOp
. There are twoapply
calls in this code.first.apply
is theapply
method in the typeT
; this is the method you control which determines what happens at each step. Thetail_op.apply
is our recursive call to either another version of thisapply
function or to the base case, depending on whatTs...
is.Note that we haven't said anything about tuples yet. We've just taken a variadic parameter pack. We're going to convert the tuple into a parameter pack using an
std::integer_sequence
(More specifically, anstd::index_sequence
). Basically, we want to take a tuple containingN
elements and convert it to a sequence of parameters of the formSo we need to get an index sequence from
0
up toN-1
inclusive (whereN-1
is ourstd::tuple_size
).That complicated-looking type alias is building our index sequence. We take the tuple's size (
std::tuple_size<std::tuple<Ts...>>::value
) and pass it tostd::make_index_sequence
, which gives us anstd::index_sequence<0, 1, 2, ..., N-1>
. Now we need to get that index sequence as a parameter pack. We can do that with one extra layer of indirection to get type inference.The second
apply
is the one outside users call. They pass a tuple and an input value. Then we construct anstd::index_sequence
of the appropriate type and pass that to the firstapply
, which uses that index sequence to access each element of the tuple in turn.Complete, runnable example
将fold-expryse应用于分配操作员
demo
您可以引入一个反向顺序的假人变量
https://godbolt.org/z/9wvshrke4“ rel =” nofollow noreferrer“> demo
Apply fold-expression to assignment operator
Demo
You can introduce an dummy variable for reverse order
Demo
没有递归的一种方式是使用折叠表达式。
不幸的是,没有呼叫组成操作员折叠。
但是您可以创建自定义类型和转移常规操作员:
使用类似于
demo
One way without recursion is to use fold expression.
Unfortunately, there is no call composition operator folding.
But you might create custom type and divert regular operator:
Usage similar to
Demo