如何将参数包中的可调用对象与数组中的相应元素作为参数匹配?

How to match callable objects out of parameter pack to corresponding element in an array as argument?

提问人:Danny 提问时间:9/9/2022 最后编辑:The Dreams WindDanny 更新时间:9/9/2022 访问量:97

问:

例如,假设我有以下内容:

template<typename ...FunctionTypes>
static void MainFunction(FunctionTypes... functions) 
{
    constexpr Uint32_t NumFunctions= sizeof...(FunctionTypes);

    std::array<double, NumFunctions> myArray;
    double arg1 = 4.2;
    int arg2= 9;
    for_each_tuple(myArray, FigureOutThisPart(myFunctions, arg1, arg2)...);
}

Where 采用元组或大小数组以及要应用于每个元组条目的函数。这部分很难实现,但有效!它的定义如下:for_each_tupleNN

namespace detail {
template <typename Tuple, std::size_t ...Indices, typename ...FunctionTypes>
constexpr void for_each_tuple_impl(Tuple&& tuple, std::index_sequence<Indices...>, FunctionTypes&&... functionsIn) {
    std::tuple<FunctionTypes...> functions = std::tie(functionsIn...);
    using swallow = int[];
    (void)swallow{
        1, // Make sure array has at least one element
        (std::get<Indices>(functions)(std::get<Indices>(std::forward<Tuple>(tuple))), void(), int{})...
    };
}
}
template <typename Tuple, typename ...Functions>
void for_each_tuple(Tuple&& tuple, Functions&&... f) {
    constexpr std::size_t N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
    static_assert(N == sizeof...(Functions), "Need one function per tuple entry");
    detail::for_each_tuple_impl(
        std::forward<Tuple>(tuple),
        std::make_index_sequence<N>{},
        std::forward<Functions>(f)...);
}

这个想法是我有一组 ,每个 都在不同的条目上操作。中的每个函数都将 、 和当前数组条目作为参数。myFunctionsmyArraymyFunctionsarg1arg2

我的目标是能够传入并进入每个值,以便这些值可以在当前数组条目的操作中使用。有没有一种理智的方法可以做到这一点?理想情况下,我希望解决方案是一个 constexpr,以便所有内容都可以在编译时得到解决。arg1arg2myFunctions

C++ 模板 泛型

评论


答:

4赞 Quimby 9/9/2022 #1

我认为这样的事情可以工作:

#include <array>
#include <functional>

template<typename ...FunctionTypes>
constexpr void MainFunction(FunctionTypes... functions) 
{
    constexpr auto NumFunctions= sizeof...(FunctionTypes);

    std::array<double, NumFunctions> myArray{};//Zero-init for now.
    double arg1 = 4.2;
    int arg2= 9;

    std::size_t i=0;
    (std::invoke(functions, arg1,arg2, myArray[i++]),...);
}

#include <iostream>

void foo(double a1, int a2, int a){
    std::cout<<"Called foo("<<a1<<','<<a2<<','<<a<<")\n";
}

int main()
{
    MainFunction(foo,foo);
    return 0;
}

折叠表达式需要 C++17,逗号需要序列点。

它可以在编译时进行评估:

constexpr void bar(double a1, int a2, int a){
}
int main()
{
    constexpr auto x = (MainFunction(bar,bar),1); // Force compile-time evaluation.
    return 0;
}

评论

1赞 Danny 9/9/2022
哦,这太棒了!选中我所有的框,不需要改变我所拥有的太多东西。非常感谢!
0赞 Quimby 9/9/2022
@Danny 请注意,通话中只能有一个表达式。不能使用单个变量跨两个数组编制索引,因为函数参数的计算顺序未指定,并且增量使该 UB。在这种情况下,只需使用两个单独的变量。i
2赞 The Dreams Wind 9/9/2022 #2

我只是在一开始就用函子制作一个元组,并将元组的一个元素与数组的相应元素相匹配:

template<std::size_t I, typename E, typename ...F, typename ...Args>
constexpr void apply(const std::array<E, sizeof...(F)>& elements,
                     const std::tuple<F...>& functions,
                     Args... args) {
    std::get<I>(functions)(std::get<I>(elements), args...);
}

template<std::size_t ...I, typename E, typename ...F, typename ...Args>
constexpr void for_each(std::index_sequence<I...>,
                        const std::array<E, sizeof...(I)>& elements,
                        const std::tuple<F...>& functions,
                        Args... args) {
    (apply<I>(elements, functions, args...),...);
}


template<typename ...F>
constexpr static void MainFunction(F... functors)
{
    constexpr std::size_t size = sizeof...(F);
    constexpr std::array<double, size> elements{};
    
    constexpr double arg1 = 4.2;
    constexpr int arg2 = 9;
    
    for_each(std::make_index_sequence<size>(),
             elements,
             std::forward_as_tuple(functors...),
             arg1, arg2);
    
}

客户端代码将如下所示:

void function(double val, double arg1, int arg2) {
    std::cout << arg1 + arg2 + 8 * val << std::endl;
}


int main() {
    MainFunction(function, [](double val, double arg1, double arg2) {
        std::cout << val / arg1 + arg2 << std::endl;
    });
    return 0;
}

只要参数函数是 ,实现也是:constexprconstexpr

template<typename ...F>
constexpr static auto MainFunction(F... functors) { ... return elements;  }
constexpr void function(double val, double arg1, int arg2) { ... }

int main() {
    // Retrieves the constexpr elements array from the function
    constexpr auto val = MainFunction(function, [](double, double, int){ ... });
}
1赞 Fureeish 9/9/2022 #3

您可以使用 std::bind_front 构造一个具有占位符参数的函数,这是一个更接近您最初预期的解决方案。然而,这有一个缺点,即不能在上下文中运行:constexpr

template <std::size_t N, typename T, typename... Functions>
auto for_each_tuple(std::array<T, N> const& array, Functions... functions) {
    auto index = 0uz;
    (std::invoke(functions, array[index++]), ...);
}

template<typename ...FunctionTypes>
static void MainFunction(FunctionTypes... functions)
{
    constexpr std::uint32_t NumFunctions= sizeof...(FunctionTypes);

    std::array<double, NumFunctions> myArray{};
    double arg1 = 4.2;
    int arg2= 9;
    for_each_tuple(myArray, std::bind_front(functions, arg1, arg2)...);
}

auto main() -> int {
    auto print_thrice = [](double d1, int i2, double arr) {
        std::cout << d1 << ' ' << i2 << ' ' << arr << '\n';
    };

    MainFunction(print_thrice, print_thrice, print_thrice);
}