分发参数包以调用两个函子

Distribute argument parameter pack to invoke two functors

本文关键字:两个 参数 调用      更新时间:2023-10-16

我试图通过一个给定的参数包(typename Args... args)调用两个函数对象,提供了一个整数参数来标记我需要拆分包以正确调用两个功能对象的边界。

考虑以下示例:

Args... = <int, int, std::vector<int>, std::vector<int>>
unsigned Bounds = 2;
functor Foo (left) and Bar (right)
// Foo is invoked with <int, int>
// Bar is invoked with <std::vector<int>, std::vector<int>>
// An evaluator template class is invoked to merge the result of both,
// for example with an add (operator+) operation

我的想法是创建两个整数序列,并使用std::get用这两个整数顺序同时调用两个函数对象:

// Sequence creator
template<unsigned Position, unsigned Count, unsigned... Pack>
struct make_sequence
    : std::conditional<
        Count == 0,
        std::common_type<sequence<Pack...>>,
        make_sequence<Position + 1, Count - 1, Pack..., Position>
    >::type { };
// Create a sequence from inclusive from to exclusive to
template<unsigned InclusiveFrom, unsigned ExclusiveTo>
using make_sequence_from_to_t = typename make_sequence<
    InclusiveFrom,
    (ExclusiveTo <= InclusiveFrom) ? 0U : (ExclusiveTo - InclusiveFrom)
>::type;
template<typename LeftType, typename RightType, unsigned Bounds, typename Evaluator>
class distribute_functor
{
    LeftType left_;
    RightType right_;
    template<unsigned... LeftSeq, unsigned... RightSeq, typename... Args>
    auto internal_invoke(sequence<LeftSeq...>, sequence<RightSeq...>, Args... args)
    {
        return Evaluator::evaluate(left_(std::get<LeftSeq>(args)...),
//                                 ~~~~~~~~~~~~~~~^^^^^^^~~^^^^~~~~~
//                                 error C3528: 'LeftSeq': the number of
//                                 elements in this pack expansion does not
//                                 match the number of elements in 'args'
                                   right_(std::get<RightSeq>(args)...));
    }
public:
    template<typename Left, typename Right>
    distribute_functor(Left left, Right right)
        : left_(std::forward<Left>(left)), right_(std::forward<Right>(right)) { }
    template<typename... Args>
    auto operator() (Args... args)
    {
        return internal_invoke(make_sequence_from_to_t<0, Bounds>{},
                               make_sequence_from_to_t<Bounds, sizeof...(Args)>{},
                               std::forward<Args>(args)...);
    }
};

但是,VisualStudio14编译器抱怨arguments包和序列中的参数计数不匹配:

错误C3528:"LeftSeq":此包扩展中的元素数与"args"中的元素数目不匹配

仍然有一种方法可以使用std::tuple进行函子调用,我不喜欢这种方法

是否有其他或更好的方法可以在一个参数包的一步中部分调用两个函数对象

std::get不能以这种方式使用。

你应该这样写internal_invoke

template<unsigned... LeftSeq, unsigned... RightSeq, typename ArgsAsTuple>
auto internal_invoke(sequence<LeftSeq...>, sequence<RightSeq...>,ArgsAsTuple&& args) const
{
  return Evaluator::evaluate(left_(std::get<LeftSeq>(args)...),
                             right_(std::get<RightSeq>(args)...));
}

并用forward_as_tuple:调用它

return internal_invoke(make_sequence_from_to_t<0, Bounds>{},
                       make_sequence_from_to_t<Bounds, sizeof...(Args)>{},
                       std::forward_as_tuple(args...));

解释:

必须分别展开两个不同浓度的参数包。当您编写std::get<LeftSeq>(args)...时,您尝试将不同arity的包展开在一起。这是不可能的。你应该写std::get<LeftSeq>(args... /* 1st expand/) ... /* 2nd expand */。这在语法上是正确的,但与std::get API不匹配。std::forward_as_tuple可以帮助您,并且是专门为这些类型的用例编写的。

编辑:

如果您想避免使用元组,那么您必须编写自己版本的std::get以满足您的需要,前提是您可以像上面解释的那样正确地扩展参数。