Как привязать параметры рекурсивно к функции? - PullRequest
5 голосов
/ 22 сентября 2019

Мне нужно взять массив входных аргументов размера 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?

Редактировать

Я получил это благодаря Аконкагуа и Филиппу Ленку!Вот рабочий код:

#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;
} 

Edit2

Работает также с использованием кортежей:

#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;
}

Ответы [ 2 ]

5 голосов
/ 22 сентября 2019

Если я правильно понял ваш вопрос, вы на самом деле намереваетесь разложить массив на отдельные параметры.Если это так, вы можете сделать это с помощью 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, вы можете реализовать свой собственный вариант. Этот ответ объясняет, как вы могли это сделать.

2 голосов
/ 22 сентября 2019

Похоже, вы ищете std :: apply (или, может быть, лямбду, заключающую в него вызов + аргументы)

Если ваш вопрос касается 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 даже содержит реализацию, которую можно использовать в значительной степени, как показано: (значительно упрощено для вашего случая, используя константные ссылки вместо правильной совершенной пересылки, чтобы быть более кратким и ясным о намерениях)и используемый метод, без std :: invoke (другое дополнение C ++ 17) и только для массива, чтобы избежать tuple_size (который не был определен для std :: array до C ++ 17))

#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 (опять же, упрощенная и крайне неэффективная версия, ответ 1016 *, связанный с Aconcagua , содержит многолучшее и подробное объяснение):

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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...