2015-07-10 5 views
1

Есть ли способ сохранить набор std :: массивов любой (но постоянной) длины, где длина массива позже может быть использована в constexpr ?Хранение набора std :: массивов любой (но постоянной) длины

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

Пример кода:

#include <iostream> 
#include <string> 
#include <vector> 
#include <array> 
#include <algorithm> 

// Class storing an array of any (but constant) size 
struct A { 
    const std::vector<std::string> container; 
    const int container_size; 
    A(const std::vector<std::string>& v, int size) : container(v), container_size(size) { } 
}; 

int main() { 
    // List of variable length const arrays and their sizes 
    std::vector<A> myAList { 
     A({ std::string("String1"), std::string("String2") }, 2), 
     A({ std::string("String1") }, 1) 
    }; 

    // How would I go about using the size of each array in a constexpr? 
    for (auto const& a : myAList) { 
    // Example constexpr: 
    // somefunc(std::make_index_sequence<a.container_size>{}); 

    // 2nd example converting to actual std::array 
    // std::array<std::string, a.container_size> arr; 
    // std::copy_n(std::make_move_iterator(a.begin()), a.container_size, arr.begin()); 
    } 

    return 0; 
} 

UPDATE:

Более подробно было предложено, так что здесь идет. Мне все равно, как определяется массив, все, что работает ... Точный constexpr используется в примере кода, std::make_index_sequence<CONSTEXPR>{}. Я просто знаю, что у меня есть набор постоянных массивов, которые определены во время компиляции, и как-то можно будет ссылаться на их длину в другом месте в constexpr.

Heck, я на самом деле буду хорошо только с хранением длины:

// Class storing an array size 
struct A { 
    A(int size) : container_size(size) { } 
    const int container_size; 
}; 

int main() { 
    // List of lengths 
    std::vector<A> mySizeList { 2, 1 }; 

    for (auto const& a : mySizeList) { 
    // somefunc(std::make_index_sequence<a.container_size>{}); 
    } 
    return 0; 
} 
+0

simpke Ответ Вы не можете.Более сложный ответ сводится к «возможно, в зависимости от того, какие именно операции constexpr и как именно вы создаете указанные массивы, а также другие детали, которые вы не включили», а реализация сложна. Вы только собираетесь назвать один конкретный 'somefunc' именно таким образом? – Yakk

+0

@Yakk 'somefunc' - это функция вариационного шаблона, которая применяет аргументы к функции, вызываемой с помощью указателя функции. Мне нужна последовательность индексов времени компиляции для распаковки аргументов, поэтому требуется 'make_index_sequence'. – Oystein

+0

Это да, или нет? ** «Вы только собираетесь назвать один конкретный« somefunc »именно таким образом?» ** Я принимаю «да», и я добавил ответ ниже. Он также передается в 'const &' в 'vector', если вам это тоже нужно. – Yakk

ответ

3

Это предполагает, что вы хотите вызвать somefunc с обоими const& к std::vector и index_sequence, и что вы хотите только позвонить somefunc , Он также предлагает поддержку somefunc с расширенной подписью (просто передайте третий аргумент с возвращаемым значением и/или дополнительными аргументами, которые будут переданы после std::vector<T> const&).

Невозможность выровнять размер вектора с пропущенным размером приведет к плохому, если размер, пройденный, длиннее вектора.

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

Используемая техника называется стиранием типа или концепциями времени выполнения. Я стираю концепцию вызова вашего some_func с соответствующей последовательностью индексов во время построения и сохраняю эту операцию стирания в f.

template<size_t N> 
using size=std::integral_constant<size_t, N>; 

template< 
    class T, template<class...>class Operation, 
    class Sig=std::result_of_t<Operation<size<0>>(std::vector<T> const&)>() 
> 
struct eraser; 

template<class T, template<class...>class Operation, class R, class...Args> 
struct eraser<T,Operation, R(Args...)> { 
    std::vector<T> data; 
    R(*f)(eraser const*, Args&&...); 
    R operator()(Args...args)const{ 
    return f(this, std::forward<Args>(args)...); 
    } 
    template<size_t N> 
    eraser(std::initializer_list<T> il, size<N>): 
    eraser(il.begin(), size<N>{}) 
    {} 
    template<class...Ts> 
    eraser(T t0, Ts... ts): 
    eraser(
     {std::forward<T>(t0), std::forward<Ts>(ts)...}, 
     size<sizeof...(ts)+1>{} 
    ) 
    {} 
    template<size_t N> 
    eraser(T const* ptr, size<N>): 
    data(ptr, ptr+N), 
    f([](eraser const*self, Args&&...args)->R{ 
     return Operation<size<N>>{}(self->data, std::forward<Args>(args)...); 
    }) 
    {} 
}; 
template<class T, size_t ... Is> 
void some_func(std::vector<T> const&, std::index_sequence<Is...>) { 
    std::cout << "called! [#" << sizeof...(Is) << "]\n"; 
} 
template<class N> 
struct call_some_func { 
    template<class T> 
    void operator()(std::vector<T> const& v) const { 
    some_func(v, std::make_index_sequence<N{}>{}); 
    } 
}; 


int main() { 
    using A = eraser<std::string, call_some_func>; 
    // List of variable length const arrays and their sizes 
    std::vector<A> myAList { 
    A({ std::string("String1"), std::string("String2") }, size<2>{}), 
    A({ std::string("String1") }, size<1>{}) 
    }; 

    std::cout << "Objects constructed. Loop about to start:\n"; 

    // How would I go about using the size of each array in a constexpr? 
    for (auto const& a : myAList) { 
    a(); 
    } 
} 

live example

+0

Это какой-то симпатичный код шаблона завалов! После множества настроек и попыток понять ваш код я в конечном итоге использовал некоторые из концепций, и теперь он прекрасно работает! – Oystein

+0

Я не предполагаю, что вы знаете способ префикса определения 'size {}' integ_constant (кроме макросов)? – Oystein

+1

'template constexpr size sz() {return {}; } 'дает вам функцию' sz', где вы можете 'sz <0>()', если вы считаете это более красивым? Вы также можете передать 'std :: string' в вариационный ctor 'eraser', который подсчитывает аргументы для вас. Таким образом, вы получите 'std :: vector myAList {{" String1 "," String2 "}, {" String1 "}}', который кажется более красивым. Вложенная версия добавлена ​​к сообщению выше. – Yakk

0

Я предполагаю, что вы хотите что-то вроде dynarray, который умирающий.

Вы, конечно же, не можете использовать свой размер в константном выражении, потому что это не константа времени компиляции (это то, к чему относится «dyn» в названии).

Чтобы использовать container_size в постоянном выражении типа A должен быть буквальным типом, поэтому он должен иметь constexpr конструктор, который собирается быть очень трудно сделать, если вы хотите, чтобы передать его vector!

Вы могли бы сделать что-то вроде этого, но это довольно противно:

#include <array> 

struct A 
{ 
    void* ap; 
    std::size_t n; 
    template<std::size_t N> 
     constexpr A(std::array<int, N>& a) : ap(&a), n(N) { } 
    template<std::size_t N> 
     std::array<int, N>& get() const { return *static_cast<std::array<int, N>*>(ap); } 
    constexpr std::size_t size() const { return n; } 
}; 

int main() 
{ 
    static std::array<int, 3> a{ 1, 2, 3 }; 
    constexpr A aa(a); 
    constexpr int len = aa.size(); // OK, this is a constant expression 
    std::array<int, aa.size()>& r = aa.get<aa.size()>();   
} 
+0

Я вижу, может быть, отвратительный способ пойти один раз ... – Oystein

+0

Я не понимаю вопроса, даже посмотрев на два текущих ответа. Я не думаю, что я глуп. –