迭代 C++ 可变参数模板
Iterate over C++ variadic template
我有以下内容:
template<typename FIRST, typename SECOND>
Sender *createSenderChain() {
return new FIRST(new SECOND());
}
是否可以使模板可变:
template<typename FIRST, typename ...Args>
Sender *createSenderChain() {
return new FIRST(new SECOND(new THIRD(new ...)) <-- This is the pattern I want,
but how should it be done
using the args list?
}
您可以为此使用递归!
猜测你对 Sender
的定义:
struct Sender { ~Sender() {} };
struct A : Sender { A(Sender* = nullptr) {} };
struct B : Sender { B(Sender* = nullptr) {} };
struct C : Sender { C(Sender* = nullptr) {} };
// Base case
template <typename T>
Sender* createSenderChain()
{
return new T();
}
// Recursive case
template <typename T1, typename T2, typename ...Ts>
Sender* createSenderChain()
{
return new T1(createSenderChain<T2, Ts...>());
}
int main()
{
auto ptr = createSenderChain<A, B, C>();
}
(live demo)
您可以使用不同的模板参数调用相同的可变参数模板函数:
template<typename FIRST, typename SECOND, typename ...Args>
Sender* createSenderChain() {
return new typename FIRST(createSenderChain<SECOND, Args...>());
}
template<typename FIRST>
Sender* createSenderChain() {
return new typename FIRST();
}
在第一个函数中,我们不仅显式声明了 typename FIRST
,还显式声明了 typename SECOND
,以避免将此实现与 createSenderChain<T>
调用匹配,因为可变参数部分可能与空列表匹配类型。
由于您的问题没有指定使用哪种 C++ 变体,因此此解决方案使用 C++17。这意味着它可以避免递归。
我们通过二元折叠来解析函数的组合。为此,我们首先需要将函数对象转换为可以通过二元运算组合的对象:
template<class F>
struct compose_t {
F f;
template<class Lhs, class Rhs>
auto operator*( compose_t<Lhs> lhs, compose_t<Rhs> rhs ) {
auto r =
[lhs = std::move(lhs).f, rhs = std::move(rhs).f](auto&&...args)
->decltype(auto)
{ return lhs(rhs(decltype(args)(args)...)); }
return compose_t<decltype(r)>{ std::move(r) };
}
template<class...Args>
decltype(auto) operator()(Args&&...args){
return f(std::forward<Args>(args)...);
}
};
template<class F>
compose_t<F> compose(F f) { return {std::forward<F>(f)}; }
这将创建一个可组合函数对象,该对象由 *
.
组合而成
接下来,我们需要一个表示"construct a T on the heap"的对象,不指定如何它是由对象完成的:
template<class T>
auto maker() {
return [](auto&&...args) {
return std::make_unique<T>( decltype(args)(args)...) )
};
}
maker
returns 表示调用 make_unique<T>
稍后提供的一组参数的函数对象。我可以用原始指针做到这一点,但我拒绝了。
template<typename ...Args>
std::unique_ptr<Sender> createSenderChain() {
return (compose( maker<Args>() ) * ...)();
}
完成。请注意,我使用 unique_ptr<Sender>
s 而不是 Sender*
s,因为我拒绝提供您不应该使用的垃圾代码。
我有以下内容:
template<typename FIRST, typename SECOND>
Sender *createSenderChain() {
return new FIRST(new SECOND());
}
是否可以使模板可变:
template<typename FIRST, typename ...Args>
Sender *createSenderChain() {
return new FIRST(new SECOND(new THIRD(new ...)) <-- This is the pattern I want,
but how should it be done
using the args list?
}
您可以为此使用递归!
猜测你对 Sender
的定义:
struct Sender { ~Sender() {} };
struct A : Sender { A(Sender* = nullptr) {} };
struct B : Sender { B(Sender* = nullptr) {} };
struct C : Sender { C(Sender* = nullptr) {} };
// Base case
template <typename T>
Sender* createSenderChain()
{
return new T();
}
// Recursive case
template <typename T1, typename T2, typename ...Ts>
Sender* createSenderChain()
{
return new T1(createSenderChain<T2, Ts...>());
}
int main()
{
auto ptr = createSenderChain<A, B, C>();
}
(live demo)
您可以使用不同的模板参数调用相同的可变参数模板函数:
template<typename FIRST, typename SECOND, typename ...Args>
Sender* createSenderChain() {
return new typename FIRST(createSenderChain<SECOND, Args...>());
}
template<typename FIRST>
Sender* createSenderChain() {
return new typename FIRST();
}
在第一个函数中,我们不仅显式声明了 typename FIRST
,还显式声明了 typename SECOND
,以避免将此实现与 createSenderChain<T>
调用匹配,因为可变参数部分可能与空列表匹配类型。
由于您的问题没有指定使用哪种 C++ 变体,因此此解决方案使用 C++17。这意味着它可以避免递归。
我们通过二元折叠来解析函数的组合。为此,我们首先需要将函数对象转换为可以通过二元运算组合的对象:
template<class F>
struct compose_t {
F f;
template<class Lhs, class Rhs>
auto operator*( compose_t<Lhs> lhs, compose_t<Rhs> rhs ) {
auto r =
[lhs = std::move(lhs).f, rhs = std::move(rhs).f](auto&&...args)
->decltype(auto)
{ return lhs(rhs(decltype(args)(args)...)); }
return compose_t<decltype(r)>{ std::move(r) };
}
template<class...Args>
decltype(auto) operator()(Args&&...args){
return f(std::forward<Args>(args)...);
}
};
template<class F>
compose_t<F> compose(F f) { return {std::forward<F>(f)}; }
这将创建一个可组合函数对象,该对象由 *
.
接下来,我们需要一个表示"construct a T on the heap"的对象,不指定如何它是由对象完成的:
template<class T>
auto maker() {
return [](auto&&...args) {
return std::make_unique<T>( decltype(args)(args)...) )
};
}
maker
returns 表示调用 make_unique<T>
稍后提供的一组参数的函数对象。我可以用原始指针做到这一点,但我拒绝了。
template<typename ...Args>
std::unique_ptr<Sender> createSenderChain() {
return (compose( maker<Args>() ) * ...)();
}
完成。请注意,我使用 unique_ptr<Sender>
s 而不是 Sender*
s,因为我拒绝提供您不应该使用的垃圾代码。