2015-02-10 2 views
2

Здесь очень приятно (не мой) пример того, как и можно расширить (или «взрываются») кортеж в качестве аргументов функции:«распаковать» станд :: массив <T,N>, как аргументы функции

template<int ...I> struct index_tuple_type { 
    template<int N> using append = index_tuple_type<I..., N>; 
}; 

template<int N> struct make_index_impl { 
    using type = typename make_index_impl<N-1>::type::template append<N-1>; 
}; 

template<> struct make_index_impl<0> { using type = index_tuple_type<>; }; 

template<int N> using index_tuple = typename make_index_impl<N>::type; 

template <typename I, typename ...Args> 
struct func_traits; 

template <typename R, int ...I, typename ...Args> 
struct func_traits<R, index_tuple_type<I...>, Args...> { 
    template <typename TT, typename FT> 
    static inline R call(TT &&t, FT &&f) { 
    return f(std::get<I>(std::forward<TT>(t))...); 
    } 
}; 

template< 
    typename FT, 
    typename ...Args, 
    typename R = typename std::result_of<FT(Args&&...)>::type 
> 
inline R explode(std::tuple<Args...>& t, FT &&f) { 
    return func_traits<R, index_tuple<sizeof...(Args)>, Args...> 
    ::call(t, std::forward<FT>(f)); 
} 

, то вы можете использовать это как так:

void test1(int i, char c) { 
    printf("%d %c\n", i, c); 
} 

int main() { 
    std::tuple<int, char> t1{57, 'a'}; 
    explode(t1, test1); 
} 

Live version

Я бродил, как вы могли бы сделать то же самое с std::array поскольку оно совсем как кортеж. std::get<N> работает с std::array, поэтому я подумал, что было бы легко изменить это решение. Но что-то вроде этого не работает:

template< 
    typename FT, 
    typename Arg, 
    std::size_t I, 
    typename R = typename std::result_of<FT(Arg&&)>::type 
> 
inline R explode(std::array<Arg, I>& t, FT &&f) { 
    return func_traits<R, index_tuple<I>, Arg>:: 
    call(t, std::forward<FT>(f)); 
} 

void test2(int i1, int i2) { 
    printf("%d %d\n", i1, i2); 
} 

int main() { 
    std::array<int, int> t1{1, 2}; 
    explode(t2, test1); 
} 

из части std::result_of<FT(Arg&&)>::type. Аргумент типа Arg&& неверен и result_of не имеет поля type. Для кортежа Args&&... расширен, но теперь он должен быть «повторен» I раз. Есть ли способ сделать это, используя result_of, чтобы возвращаемый тип можно было вычесть?

Также мне было интересно, имея инструменты, чтобы «распаковать» tuple и array было бы возможно «распаковать» рекурсивно (возможно, с использованием enable_if) структуры, как tuple<array<int, 2>, tuple<array<double,3>, ... и так далее? Какое-то дерево, где tuple и array - ветки, а другие - листья?

+1

'std :: forward' с ссылкой lvalue, закодированной как аргумент шаблона типа, равный« no-op ». и вы, вероятно, хотели поставить 't' в качестве своего аргумента. и btw, используйте [trailing return type like here] (http://coliru.stacked-crooked.com/a/5af1aac65ba014d9) вместо –

+0

Вы считали вместо этого выполнение функции, которая скрывает массив до кортежа ссылок? – Hurkyl

+0

@PiotrS. исправил, что 'forward', мой плохой расширяющийся макрос ошибочно. @Hurkyl Да, я подумал об этом, но это не так. –

ответ

8
// enable argument dependent lookup on `get` call: 
namespace aux { 
    using std::get; 
    template<size_t N, class T> 
    auto adl_get(T&&)->decltype(get<N>(std::declval<T>())); 
} 
using aux::adl_get; 
template<class F, class TupleLike, size_t...Is> 
auto explode(F&& f, TupleLike&& tup, std::index_sequence<Is...>) 
-> std::result_of_t< F(decltype(adl_get<Is>(std::forward<TupleLike>(tup)))...) > 
{ 
    using std::get; // ADL support 
    return std::forward<F>(f)(get<Is>(std::forward<TupleLike>(tup))...); 
} 

- первый шаг. std::index_sequence - это C++ 14, но его легко реализовать на C++ 11.

Следующие шаги также просты.

Во-первых, класс признаков, который диктует, какие типы похожи на кортежи. Я бы идти вперед и просто утка типа использовать их, но ряд функций и признаков классов мы будем использовать не SFINAE дружественных:

template<class T> 
struct tuple_like:std::false_type{}; 
template<class... Ts> 
struct tuple_like<std::tuple<Ts...>>:std::true_type{}; 
template<class... Ts> 
struct tuple_like<std::pair<Ts...>>:std::true_type{}; 
template<class T, size_t N> 
struct tuple_like<std::array<T,N>>:std::true_type{}; 

Далее, перегрузка explode, который работает только на tuple_like типов:

template<class F, class TupleLike, 
    class TupleType=std::decay_t<TupleLike>, // helper type 
    class=std::enable_if_t<tuple_like<TupleType>{}>> // SFINAE tuple_like test 
auto explode(F&& f, TupleLike&& tup) 
-> decltype(
    explode(
    std::declval<F>(), 
    std::declval<TupleLike>(), 
    std::make_index_sequence<std::tuple_size<TupleType>{}>{} 
) 
) 
{ 
    using indexes = std::make_index_sequence<std::tuple_size<TupleType>{}>; 
    return explode(
    std::forward<F>(f), 
    std::forward<TupleLike>(tup), 
    indexes{} 
    ); 
} 

Если вам не хватает поддержки constexpr вам необходимо изменить некоторые {} к ::value.

Вышеупомянутый трюк для пар, массивов или кортежей. Если вы хотите добавить поддержку для других типов, похожих на кортеж, просто добавьте специализацию в tuple_like и убедитесь, что std::tuple_size специально спроектирован для вашего типа, а get<N> перегружен ADL (в охватываемом пространстве имен типа).


std::make_index_sequence также C++ 14, но легко писать в C++ 11.

template<size_t...> 
struct index_sequence{}; 
namespace details { 
    template<size_t count, size_t...Is> 
    struct mis_helper:mis_helper<count-1, count-1, Is...> {}; 
    template<size_t...Is> 
    struct mis_helper<0,Is...> { 
    using type=index_sequence<Is...>; 
    }; 
} 
template<size_t count> 
using make_index_sequence=typename details::mis_helper<count>::type; 

(это плохое QOI для C++ 14 библиотеки а, которая должна использовать, по крайней мере войти спуск, так как она требует О (п) шаблона рекурсивных инстанцирований шаблона для списка размера п. Однако, это п составляет менее 100, это не имеет значения).

std::enable_if_t<?> является C++ 14, но в C++ 11 всего typename std::enable_if<?>::type.

+1

Зачем перегружать, а не просто использовать 'TupleLike' и использовать' tuple_size'? –

+1

@ T.C. неуказанное поведение, когда 'tuple_size' передается что-то, что не является кортежем/массивом/пар, означает, что он не выполняет чистое SFINAE. И, по моему опыту, использование SFINAE выгодно для использования в этом случае. – Yakk

+0

@ T.C. там. SFINAE проверяет 'tuple_like'ness, одну пересылку' TupleType'. – Yakk