【问题标题】:How to bind parameters recursively to a function?如何递归地将参数绑定到函数?
【发布时间】:2019-09-22 14:18:02
【问题描述】:

我需要获取一个大小为 n 的输入参数数组并将其值绑定到另一个接受 n 个参数的函数。

我尝试使用 bind 将数组的元素一个一个地传递给函数,但它不起作用(有点像在循环中使用 bind_front)。

我需要的是这样的:

#include <iostream>
#include <functional>


using namespace std;

int addThreeNumbers(int a, int b, int c)
{
    return a+b+c;
}

int main()
{
    int parameters[] = {1, 2, 3};

    auto f = addThreeNumbers;

    // Bind each element from the list one by one
    for(int i=1; i<parametersLength; i++)
    {
         f = bind(f, parameters[i]);
    }

    f(); // call addThreeNumbers

    return 0;
}

解决方案需要使用任意大小的数组的参数。 使用 c++ 11 可以做到这一点吗?

编辑

多亏了 Aconcagua 和 Philipp Lenk,我才得以成功! 这是工作代码:

#include <iostream>
#include <functional>


using namespace std;

// index sequence only
template <size_t ...>
struct indexSequence
 { };

template <size_t N, size_t ... Next>
struct indexSequenceHelper : public indexSequenceHelper<N-1U, N-1U, Next...>
 { };

template <size_t ... Next>
struct indexSequenceHelper<0U, Next ... >
 { using type = indexSequence<Next ... >; };

template <size_t N>
using makeIndexSequence = typename indexSequenceHelper<N>::type;

int addThreeNumbers(int a, int b, int c)
{
    return a+b+c;
}


template <typename F, typename T, size_t N, size_t ... I>
auto dispatch(F function, T(&array)[N], indexSequence<I ...>) -> decltype(function(array[I]...))
{
    return function(array[I]...);
}

template <typename F, typename T, size_t N>
auto dispatch(F function, T(&array)[N]) -> decltype(dispatch(function, array, makeIndexSequence<N>()))
{
    return dispatch(function, array, makeIndexSequence<N>());
}


int main()
{
    int a[] = { 1, 2, 3 };
    int s = dispatch(addThreeNumbers, a);

    cout << s << endl;

    return 0;
} 

编辑2

使用元组也能正常工作:

#include <iostream>
#include <tuple>
#include <string>


using namespace std;


// index sequence only
template <size_t ...>
struct indexSequence
 { };

template <size_t N, size_t ... Next>
struct indexSequenceHelper : public indexSequenceHelper<N-1U, N-1U, Next...>
 { };

template <size_t ... Next>
struct indexSequenceHelper<0U, Next ... >
 { using type = indexSequence<Next ... >; };

template <size_t N>
using makeIndexSequence = typename indexSequenceHelper<N>::type;


template <class F, class Tuple, std::size_t... Is>
constexpr auto apply_impl(const F& f, Tuple t, indexSequence<Is...>) -> decltype(f(std::get<Is>(t)...))
{
    return f(std::get<Is>(t)...);
}

template <class F, class Tuple>
constexpr auto apply(const F& f, Tuple t) -> decltype(apply_impl(f, t, makeIndexSequence<std::tuple_size<Tuple>{}>{}))
{
    return apply_impl(f, t, makeIndexSequence<std::tuple_size<Tuple>{}>{});
}


int sum(int a, int b, string c)
{
    cout << c << endl;
    return a+b;
}


int main()
{
    auto parameters = std::make_tuple(1,2,"1+2=3");

    int s = apply(sum, parameters);

    cout << s << endl;

    return 0;
}

【问题讨论】:

  • 您的意思是“递归”还是迭代?因为这些不是同一个词,并且含义大不相同,而且您发布的代码使用迭代而不是递归。事实上,使用实际递归可能会容易得多。
  • 另外,n 是编译时定义的还是运行时定义的?因为后者实际上是不可能的(或者至少,不容易做到)。
  • 我的意思是迭代,但也可以接受递归解决方案。 N 是定义的编译时间。

标签: c++ c++11


【解决方案1】:

如果我没看错您的问题,您实际上打算将数组分解为单个参数。如果是这样,您可以使用std::index_sequence

template <typename F, typename T, size_t N, std::size_t ... I>
auto dispatch(F function, T(&array)[N], std::index_sequence<I ...>)
{
    return function(array[I]...);
}

template <typename F, typename T, size_t N>
auto dispatch(F function, T(&array)[N])
{
    return dispatch(function, array, std::make_index_sequence<N>());
}

使用示例:

int sum(int a, int b, int c)
{
    return a + b + c;
}

int a[] = { 1, 2, 3 };
int s = dispatch(sum, a);

当然,如果数组长度和函数参数个数不匹配,你会得到一个编译错误...

编辑:由于std::index_sequence 在 C++14 之前不可用,您可以实现自己的变体。 This answer 解释了如何做到这一点。

【讨论】:

  • 这正是我想要的,但遗憾的是 index_sequence 只是 c++14。有没有 c++11 的替代品?
  • 我尝试实现 index_sequence 但我得到了以下error: ‘integer_sequence’ in namespace ‘std’ does not name a template type实现是template&lt;std::size_t... Ints&gt; using index_sequence = std::integer_sequence&lt;std::size_t, Ints...&gt;;
  • @Samuel std::integer_sequence 的 C++14 不亚于 std::index_sequence... 你点击链接了吗?有一个 struct IndexSequence、一个辅助结构和一个用于辅助模板 (make ...) 的最终 using。无论您决定自己选择哪个名称,请确保始终如一地使用它们。请注意,您不得向std 命名空间添加新函数或类(现有函数的特化除外)。
【解决方案2】:

基本上你似乎在寻找std::apply(或者可能是一个包装了对它的调用+参数的lambda)

如果您的问题涉及 C++17 或更高版本,并且您的参数存储在 std::array 中,则解决方案看起来很简单:

#include <array>
#include <iostream>
#include <tuple>

int addThreeNumbers(int a, int b, int c)
{
    return a+b+c;
}

int main()
{
    std::array parameters = {1, 2, 3};

    auto f = [&]()
    {
        return std::apply(addThreeNumbers,parameters);
    };

    std::cout<<f();

    return 0;
}

当您要求使用 C++11 时,您的生活将变得更加困难,因为 std::apply 还不是标准库的一部分。但是,可以实现类似的东西,链接的 cppreference 页面甚至包含一个几乎可以使用的实现,如下所示: (针对您的情况进行了显着简化,使用 const 引用而不是适当的完美转发以更简洁明了地使用的意图和方法,没有 std::invoke(另一个 C++17 添加)并且仅用于数组,以避免 tuple_size(在 C++17 之前也没有为 std::array 定义))

#include <iostream>
#include <tuple>

int addThreeNumbers(int a, int b, int c)
{
    return a+b+c;
}

namespace detail
{
    template <class F, class T, std::size_t N, std::size_t... I>
    constexpr decltype(auto) apply_impl(const F& f, const std::array<T,N>& t, std::index_sequence<I...>)
    {
        return f(t[I]...);
    }
} 

template <class F, class T, std::size_t N>
constexpr decltype(auto) apply(const F& f, const std::array<T,N>& t)
{
    return detail::apply_impl(f, t, std::make_index_sequence<N>{});
}

int main()
{
    std::array<int,3> parameters= {1, 2, 3};

    auto f = [&]()
    {
        return apply(addThreeNumbers,parameters);
    };

    std::cout<<f();

    return 0;
}

酷,我们现在已经降到 C++14,但尝试将其编译为 C++11 会发现另一个问题:std::index_sequence 是 C++14 的添加。对我们来说幸运的是,这也不是语言添加,可以为 C++11 实现(同样,简化且效率极低的版本,Aconcagua 链接的answer 包含更好和详细的解释):

template <std::size_t... I>
struct index_sequence{};

namespace detail
{
    template <std::size_t N, std::size_t... I>
    struct index_sequence_helper
    {
        using type=typename index_sequence_helper<N-1,N-1,I...>::type;
    };

    template <std::size_t... I>
    struct index_sequence_helper<0,I...>
    {
        using type=index_sequence<I...>;
    };
}

template <std::size_t N>
using make_index_sequence=typename detail::index_sequence_helper<N>::type;

有了这个,我们终于为您的原始问题提供了一个完整且有效的 C++11 解决方案(进行了一些更小的调整,例如缺少返回类型扣除):

#include <array>
#include <iostream>

int addThreeNumbers(int a, int b, int c)
{
    return a+b+c;
}

template <std::size_t... I>
struct index_sequence{};

namespace detail
{
    template <std::size_t N, std::size_t... I>
    struct index_sequence_helper
    {
        using type=typename index_sequence_helper<N-1,N-1,I...>::type;
    };

    template <std::size_t... I>
    struct index_sequence_helper<0,I...>
    {
        using type=index_sequence<I...>;
    };
}

template <std::size_t N>
using make_index_sequence=typename detail::index_sequence_helper<N>::type;

namespace detail {
    template <class F, class T, std::size_t N, std::size_t... I>
    constexpr auto apply_impl(const F& f, const std::array<T,N>& t, index_sequence<I...>) -> decltype(f(t[I]...))
    {
        return f(t[I]...);
    }
}

template <class F, class T, std::size_t N>
constexpr auto apply(const F& f, const std::array<T,N>& t) -> decltype(detail::apply_impl(f, t, make_index_sequence<N>{}))
{
    return detail::apply_impl(f, t, make_index_sequence<N>{});
}

int main()
{
    std::array<int,3> parameters= {1, 2, 3};

    auto f = [&]()
    {
        return apply(addThreeNumbers,parameters);
    };

    std::cout<<f();

    return 0;
}

【讨论】:

  • 您的变体不包括make_index_sequence&lt;0&gt;。它可以这样做(然后产生空序列),如果你将一个减法转移到细节中(你也需要在停止专业化时跳过初始 0)。比较我提供的链接。
  • 您当然是完全正确的。谢谢 ;-)(在编辑中调整)
  • 感谢您的回答!一步一步的帮助很大。
  • 一个问题,你的答案是否也适用于 c++11 中的元组?
  • 好吧,你必须用 std::get 替换索引并使用类似 tuple_size 的东西来知道要创建哪个索引序列。但是除了那些小细节,是的,它应该可以工作
猜你喜欢
  • 1970-01-01
  • 2014-02-07
  • 2019-09-10
  • 2022-01-19
  • 1970-01-01
  • 2013-10-18
  • 2023-03-16
  • 1970-01-01
相关资源
最近更新 更多