Как развернуть кортеж в аргументы функции шаблона переменной? - PullRequest
124 голосов
/ 26 марта 2009

Рассмотрим случай шаблонной функции с аргументами шаблона variadic:

template<typename Tret, typename... T> Tret func(const T&... t);

Теперь у меня есть кортеж t значений. Как мне вызвать func(), используя значения кортежа в качестве аргументов? Я читал о функциональном объекте bind() с функцией call(), а также о функции apply() в некоторых устаревших документах. Реализация GNU GCC 4.4, кажется, имеет функцию call() в классе bind(), но по этому вопросу очень мало документации.

Некоторые люди предлагают рукописные рекурсивные хаки, но истинное значение аргументов вариационных шаблонов - возможность использовать их в случаях, подобных описанным выше.

У кого-нибудь есть решение или есть подсказка, где почитать об этом?

Ответы [ 13 ]

0 голосов
/ 15 октября 2013

Я оцениваю MSVS 2013RC, и в некоторых случаях не удалось скомпилировать некоторые из предыдущих решений, предложенных здесь. Например, MSVS не сможет скомпилировать «автоматические» возвраты, если слишком много параметров функции из-за ограничения на объем пространства имен (я отправил эту информацию в Microsoft, чтобы исправить ее). В других случаях нам нужен доступ к возврату функции, хотя это также можно сделать с помощью лямды: следующие два примера дают один и тот же результат.

apply_tuple([&ret1](double a){ret1 = cos(a); }, std::make_tuple<double>(.2));
ret2 = apply_tuple((double(*)(double))cos, std::make_tuple<double>(.2));

И еще раз спасибо тем, кто разместил здесь ответы до меня, я бы не добрался до этого без него ... так вот оно:

template<size_t N>
struct apply_impl {
    template<typename F, typename T, typename... A>
    static inline auto apply_tuple(F&& f, T&& t, A&&... a)
    -> decltype(apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...);
    }
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a)
    -> decltype(apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...);
    }
};

// This is a work-around for MSVS 2013RC that is required in some cases
#if _MSC_VER <= 1800 /* update this when bug is corrected */
template<>
struct apply_impl<6> {
    template<typename F, typename T, typename... A>
    static inline auto apply_tuple(F&& f, T&& t, A&&... a)
    -> decltype(std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...);
    }
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a)
    -> decltype((o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return (o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...);
    }
};
#endif

template<>
struct apply_impl<0> {
    template<typename F, typename T, typename... A>
    static inline auto apply_tuple(F&& f, T&&, A&&... a)
    -> decltype(std::forward<F>(f)(std::forward<A>(a)...)) {
         return std::forward<F>(f)(std::forward<A>(a)...);
    }
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply_tuple(C*const o, F&& f, T&&, A&&... a)
    -> decltype((o->*std::forward<F>(f))(std::forward<A>(a)...)) {
         return (o->*std::forward<F>(f))(std::forward<A>(a)...);
    }
};

// Apply tuple parameters on a non-member or static-member function by perfect forwarding
template<typename F, typename T>
inline auto apply_tuple(F&& f, T&& t)
-> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t))) {
     return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t));
}

// Apply tuple parameters on a member function
template<typename C, typename F, typename T>
inline auto apply_tuple(C*const o, F&& f, T&& t)
-> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t))) {
     return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t));
}
0 голосов
/ 29 апреля 2013

Как насчет этого:

// Warning: NOT tested!
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>

using std::declval;
using std::forward;
using std::get;
using std::integral_constant;
using std::size_t;
using std::tuple;

namespace detail
{
    template < typename Func, typename ...T, typename ...Args >
    auto  explode_tuple( integral_constant<size_t, 0u>, tuple<T...> const &t,
     Func &&f, Args &&...a )
     -> decltype( forward<Func>(f)(declval<T const>()...) )
    { return forward<Func>( f )( forward<Args>(a)... ); }

    template < size_t Index, typename Func, typename ...T, typename ...Args >
    auto  explode_tuple( integral_constant<size_t, Index>, tuple<T...> const&t,
     Func &&f, Args &&...a )
     -> decltype( forward<Func>(f)(declval<T const>()...) )
    {
        return explode_tuple( integral_constant<size_t, Index - 1u>{}, t,
         forward<Func>(f), get<Index - 1u>(t), forward<Args>(a)... );
    }
}

template < typename Func, typename ...T >
auto  run_tuple( Func &&f, tuple<T...> const &t )
 -> decltype( forward<Func>(f)(declval<T const>()...) )
{
    return detail::explode_tuple( integral_constant<size_t, sizeof...(T)>{}, t,
     forward<Func>(f) );
}

template < typename Tret, typename ...T >
Tret  func_T( tuple<T...> const &t )
{ return run_tuple( &func<Tret, T...>, t ); }

Шаблон функции run_tuple принимает данный кортеж и передает его элементы индивидуально для данной функции. Он выполняет свою работу путем рекурсивного вызова своих шаблонов вспомогательных функций explode_tuple. Важно, чтобы run_tuple передавал размер кортежа в explode_tuple; это число действует как счетчик количества извлекаемых элементов.

Если кортеж пуст, то run_tuple вызывает первую версию explode_tuple с удаленной функцией в качестве единственного другого аргумента. Удаленная функция вызывается без аргументов, и мы закончили. Если кортеж не пустой, большее число передается второй версии explode_tuple вместе с удаленной функцией. Выполняется рекурсивный вызов explode_tuple с теми же аргументами, за исключением того, что номер счетчика уменьшается на единицу и (ссылка на) последний элемент кортежа добавляется в качестве аргумента после удаленной функции. В рекурсивном вызове либо счетчик не равен нулю, и выполняется другой вызов с уменьшенным счетчиком, и элемент со следующей ссылкой не вставляется в список аргументов после удаленной функции, но до того, как другие вставленные аргументы или счетчик достигает ноль и удаленная функция вызывается с всеми аргументами, накопленными после нее.

Я не уверен, что у меня есть синтаксис принудительного использования определенной версии шаблона функции. Я думаю, что вы можете использовать указатель на функцию как функциональный объект; компилятор автоматически исправит это.

0 голосов
/ 04 февраля 2012

Почему бы просто не обернуть ваши переменные аргументы в класс кортежей, а затем использовать рекурсию времени компиляции (см. ссылка ), чтобы получить интересующий вас индекс. Я обнаружил, что распаковка шаблонов переменных в контейнер или коллекцию может быть небезопасно по отношению к типу гетерогенные типы

template<typename... Args>
auto get_args_as_tuple(Args... args) -> std::tuple<Args...> 
{
    return std::make_tuple(args);
}
...