Я взглянул на твой вопрос и заметил, что никто не ответил на него.Мое отклонение, чтобы ответить, заняло целый день, чтобы довести его до совершенства, но мне все равно это было нужно для моей работы.
Моей работе нужно constexpr
, и я написал ответ с учетом этого.Ваша работа не определяет это, но это не может повредить.
Что не сработало;то есть моя первая попытка
(я использую GCC-4.7 от MacPorts на 10-летнем PowerPC Mac.)
Вы можете легко включить (C ++ 11) variadicСписок параметров функции для любого типа кортежа:
template < typename Destination, typename ...Source >
constexpr
auto initialize( Source&& ...args ) -> Destination
{ return Destination{ce_forward<Source>(args)...}; }
(Шаблон функции ce_forward
аналогичен std::forward
, за исключением того, что я явно сделал его constexpr
.)
(КогдаЯ не поместил Destination
в тело, мой компилятор дал мне ошибки, связанные с невозможностью инициализации с помощью std::initialization_list
, поэтому форма, которую я сейчас имею, должна работать с любым агрегатом или конструктором из типа назначенияподдерживает.)
Но сначала нам нужно пойти другим путем, а затем использовать мой код выше для перевода обратно.Я попробовал код, подобный следующему:
template < typename Destination, typename Source, typename Size1, typename Size2, typename ...Args >
constexpr
auto fill_from_array( Source&& source, Size1 index_begin, Size2 index_end, Args&& ...args )
-> Destination
{
return ( index_begin < index_end )
? fill_from_array<Destination>( ce_forward<Source>(source), index_begin + 1, index_end, ce_forward<Args>(args)..., ce_forward<Source>(source)[index_begin] )
: initialize<Destination>( ce_forward<Args>(args)... );
}
(Так как мне также потребовалось два источника, я сделал большую версию этой функции.)
Когда я фактически запустил этот код, мой компьютер вырубилсячерез час после превышения виртуальной памяти.Я предполагаю, что это вызывает бесконечный цикл или что-то в этом роде.(Или, может быть, это конечно, но слишком много для моей древней системы.)
Моя вторая попытка
Я рыскал ТАК, пока не нашел материал, который мог бы пригодиться:
и нашел решение из тех, что я прочитал: Разбор строк во время компиляции - Часть I .По сути, мы используем вариант моего initialize
шаблона функции выше;вместо того, чтобы основывать инициализаторы исключительно на параметрических параметрических параметрах, мы используем отображение из вариабельных параметров шаблона.
Самый простой источник отображения - неотрицательные целые числа:
#include <cstddef>
template < std::size_t ...Indices >
struct index_tuple
{ using next = index_tuple<Indices..., sizeof...(Indices)>; };
template < std::size_t Size >
struct build_indices
{ using type = typename build_indices<Size - 1>::type::next; };
template < >
struct build_indices< 0 >
{ using type = index_tuple<>; };
Шаблон класса index_tuple
это то, что мы будем передавать для отображения, в то время как шаблон класса build_indices
помещает экземпляры index_tuple
в правильный формат:
index_tuple<>
index_tuple<0>
index_tuple<0, 1>
index_tuple<0, 1, 2>
...
Мы создаем index_tuple
объекты с шаблоном функции:
template < std::size_t Size >
constexpr
auto make_indices() noexcept -> typename build_indices<Size>::type
{ return {}; }
Мы используем указанные index_tuple
объекты для их вклада в заголовок шаблона шаблона функции:
#include <array>
template < std::size_t N, std::size_t M, std::size_t ...Indices >
constexpr
std::array<char, N + M - 1u>
fuse_strings_impl( const char (&f)[N], const char (&s)[M], index_tuple<Indices...> );
Третий параметр не получает имя, потому что нам не понадобитсясам объект.Нам просто нужно "std :: size_t ... Indices" в заголовке.Мы знаем, что при расширении превратится в «0, 1, ..., X».Мы передадим это упорядоченное расширение в вызов функции, который будет расширен до необходимых инициализаторов.В качестве примера, давайте посмотрим на определение функции выше:
template < std::size_t N, std::size_t M, std::size_t ...Indices >
constexpr
std::array<char, N + M - 1u>
fuse_strings_impl( const char (&f)[N], const char (&s)[M], index_tuple<Indices...> )
{ return {{ get_strchr<Indices>(f, s)... }}; }
Мы будем возвращать array
с первым элементом как get_strchr<0>(f,s)
, вторым как get_strchr<1>(f,s)
, и такна.Обратите внимание, что это имя функции оканчивается на «_impl», потому что я скрываю использование index_tuple
и гарантирую правильный базовый случай, вызывая публичную версию:
template < std::size_t N, std::size_t M >
constexpr
std::array<char, N + M - 1u>
fuse_strings( const char (&f)[N], const char (&s)[M] )
{ return fuse_strings_impl(f, s, make_indices<N + M - 2>()); }
И вы можете попытаться кодировать так:
#include <iostream>
#include <ostream>
int main()
{
using std::cout;
using std::endl;
constexpr auto initialize_test = initialize<std::array<char, 15>>( 'G',
'o', 'o', 'd', 'b', 'y', 'e', ',', ' ', 'm', 'o', 'o', 'n', '!', '\0' );
constexpr char hello_str[] = "Hello ";
constexpr char world_str[] = "world!";
constexpr auto hw = fuse_strings( hello_str, world_str );
cout << initialize_test.data() << endl;
cout << hw.data() << endl;
}
Есть некоторые тонкости, за которыми нужно следить.
- Ваши декларации
const(expr) char str[] = "Whatever";
должны использовать []
вместо *
, поэтому компилятор распознает ваш объект каквстроенный массив, а не как указатель на неизвестную (фиксированную) память времени выполнения. - Поскольку встроенные массивы нельзя использовать в качестве возвращаемых типов, вы должны использовать
std::array
какзамена.Проблема в том, когда вам нужно использовать результат для последующего слияния.У объекта std::array
должен быть общедоступный элемент нестатических данных соответствующего встроенного типа массива, но имя этого элемента не указано в стандарте и, вероятно, не соответствует.Таким образом, вы должны создать std::array
аналог, чтобы официально избежать хаков. - Нельзя использовать один и тот же код для общих объединений массивов и строковых объединений.Строка - это массив
char
, где последний элемент должен быть '\0'
.Эти значения NUL
должны быть пропущены при чтении из строк, но добавлены при записи.Вот почему нечетные 1 и 2 появляются в моем коде.Для общих объединений массивов (включая нестроковые char
единицы) каждый элемент в каждом массиве должен читаться, и никакие дополнительные элементы не должны добавляться в объединенный массив.из get_strchr
: template < std::size_t N >
constexpr
char ce_strchr( std::size_t i, const char (&s)[N] )
{
static_assert( N, "empty string" );
return (i < ( N - 1 )) ? s[i] : throw "too big";
}
template < std::size_t N, std::size_t M, std::size_t ...L >
constexpr
char ce_strchr( std::size_t i, const char (&f)[N], const char (&s)[M], const char (&...t)[L] )
{
static_assert( N, "empty string" );
return (i < ( N - 1 )) ? f[i] : ce_strchr(i + 1 - N, s, t...);
}
template < std::size_t I, std::size_t N, std::size_t ...M >
constexpr
char get_strchr( const char (&f)[N], const char (&...s)[M] )
{ return ce_strchr(I, f, s...); }
(надеюсь, вы прочтете это).