2015-08-31 3 views
2

Есть ли переносимый способ создания std::tuple (действительно std::array) из содержимого контейнера? Такой кортеж позволял бы std::apply извлекать аргументы функции из контейнера.Сгенерировать std :: tuple из стандартного контейнера

Моя первая попытка, использующая хвостовую рекурсию, терпит неудачу с ошибкой компилятора: «Рекурсивный экземпляр шаблона превышает максимальный ...».

Я не мог получить вторую попытку (std::for_each с изменяемым лямбда-кортежем) для компиляции с желаемыми результатами.

Я предполагаю, что что-то похожее на то, как boost::mpl обрабатывает вариационные метафайлы (например, магию с использованием boost::preprocessor), можно заставить работать, но это так C++ 03. Я надеюсь, что есть лучшее решение.

Функция подписи будет выглядеть примерно так:

std::list<int> args_as_list = {1, 2, 3, 4}; 
auto tpl = args_as_tuple(args_as_list); 

где тип tpl является std::array<int const, 4>.

+4

* станд :: кортеж (на самом деле станд :: массив) * Что вы имеете в виду под этим? std :: uple и std :: array - это * не * то же самое. – Borgleader

+4

Tuple - это время компиляции, вы не можете сказать во время компиляции, сколько элева будет в списке. В любом случае вы можете использовать std :: llist :: value_type – Melkon

+0

«std :: array» поддерживает интерфейс 'std :: tuple'. Очевидно, что все элементы в контейнере являются одним и тем же типом 'container :: value_type'. Так что истинный кортеж, или 'std :: array' оба хороши. – KentH

ответ

3

Короткий ответ: нет, это невозможно.

Объяснение: оба std::tuple и std::array требуют время компиляции информация о количестве элементов. std::list или std::vector может предоставить только время выполнения информация о количестве элементов.

Ваша функция args_as_tuple должна быть шаблоном, принимая количество ожидаемых аргументов в качестве аргумента шаблона (args_as_tuple<4>(args_as_list)).

Хотя наличие количества аргументов в качестве аргумента шаблона кажется суровым, но в случае вашего примера это совершенно очевидно - количество аргументов функции (функция, предоставленная std::apply) должно быть известно и во время компиляции.
Для получения более общего кода вы можете использовать: function-traits или код от this answer.
Или использовать std::array от попрошайничества вместо std::list (много общего кода шаблона, но хорошее время компиляции проверки)

1

Число элементов в std::tuple или std::array, является частью его типа информации. Следовательно, ваша предложенная выше функция args_as_tuple должна быть каким-то образом шаблоном, и каждый разный возможный размер результата потребует другого экземпляра этого шаблона. Поэтому вы не можете создавать программу, которая может поддерживать произвольно много размеров кортежей, если код этой программы не бесконечен (это невозможно).

Если вы интересуетесь только диапазоном значений int, скажем, вы можете создать экземпляр шаблона 4 миллиарда раз, но тогда ваш исполняемый файл будет иметь размер не менее 4 гигабайт.

Если вы действительно только заботиться о несколько различных размеров векторов в вашей реальной программе, вы можете создать экземпляр только те шаблоны и писать код преобразования, что случаи на значении std::list::size() и вызывает соответствующую функцию (утомительная).

Но ваш точный фрагмент кода

std::list<int> args_as_list = {1, 2, 3, 4}; 
auto tpl = args_as_tuple(args_as_list); 

может никогда работать в C++. Поскольку в C++ все переменные должны иметь известный тип, определенный во время компиляции. Даже если вы используете ключевое слово auto, то auto должен быть разрешен во время компиляции фиксированным типом, что означает фиксированный размер, если он является кортежем или массивом, независимо от того, какой шаблон шаблона использует выражение args_as_tuple.

1

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

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

#include <utility> 

template <int N> 
using Int = std::integral_constant<int, N>; 

template <typename T> 
struct arity : arity<decltype(&T::operator())> {}; 

template <typename T, typename RT, typename...Args> 
struct arity<RT(T::*)(Args...) const> 
{ 
    // could enforce maximum number of arguments 
    static constexpr int value = sizeof...(Args); 
}; 

template <typename F, int N = arity<F>::value> 
struct eval_container 
{ 
    eval_container(F const& f) : f(f) {} 
    eval_container(F&& f) : f(std::move(f)) {} 

    template <typename Iter, typename I, typename...Args> 
    auto operator()(Iter&& iter, I, Args&&...args) const 
    { 
     // assert(iter != end) 
     auto&& arg = *iter++; 
     return (*this)(std::forward<Iter>(iter) 
        , Int<I()-1>{} 
        , std::forward<Args>(args)... 
        , arg); 
    } 

    template <typename Iter, typename...Args> 
    auto operator()(Iter&&, Int<0>, Args&&...args) const 
    { 
     // assert(iter == end) 
     return f(std::forward<Args>(args)...); 
    } 

    template <typename C> 
    auto operator()(C const& container) const 
    { 
     return (*this)(container.begin(), Int<N>{}); 
    } 

    F f; 
}; 

}

Смежные вопросы