Возможность создавать строки и управлять ими во время компиляции в C ++ имеет несколько полезных приложений. Хотя в C ++ можно создавать строки во время компиляции, этот процесс очень громоздкий, поскольку строку необходимо объявить как последовательность символов с переменным числом аргументов, например
using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>;
Такие операции, как конкатенация строк, извлечение подстроки и многие другие, можно легко реализовать как операции над последовательностями символов. Можно ли более удобно объявлять строки времени компиляции? Если нет, то есть ли в разработке предложение, которое позволило бы удобное объявление строк времени компиляции?
Почему существующие подходы терпят неудачу
В идеале мы хотели бы иметь возможность объявлять строки времени компиляции следующим образом:
// Approach 1
using str1 = sequence<"Hello, world!">;
или, используя определенные пользователем литералы,
// Approach 2
constexpr auto str2 = "Hello, world!"_s;
где decltype(str2)
будет constexpr
конструктор. Возможно реализовать более беспорядочную версию подхода 1, воспользовавшись тем, что вы можете сделать следующее:
template <unsigned Size, const char Array[Size]>
struct foo;
Однако у массива должна быть внешняя связь, поэтому, чтобы подход 1 работал, нам нужно было бы написать что-то вроде этого:
/* Implementation of array to sequence goes here. */
constexpr const char str[] = "Hello, world!";
int main()
{
using s = string<13, str>;
return 0;
}
Что и говорить, это очень неудобно. Подход 2 практически невозможно реализовать. Если бы мы объявили constexpr
буквальный оператор ( ), как бы мы указали возвращаемый тип? Поскольку нам нужен оператор для возврата переменной последовательности символов, нам нужно будет использовать const char*
параметр для указания типа возвращаемого значения:
constexpr auto
operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */
Это приводит к ошибке компиляции, потому что s
это не файл constexpr
. Попытка обойти это, выполнив следующие действия, мало помогает.
template <char... Ts>
constexpr sequence<Ts...> operator"" _s() { return {}; }
Стандарт требует, чтобы эта конкретная форма буквального оператора зарезервирована для целочисленных типов и типов с плавающей запятой. Пока 123_s
работал abc_s
бы, не стал бы. Что, если мы полностью откажемся от пользовательских литералов и просто будем использовать обычную constexpr
функцию?
template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using `array` */
Как и раньше, мы сталкиваемся с проблемой, заключающейся в том, что массив, который теперь является параметром constexpr
функции, сам по себе больше не является constexpr
типом.
Я считаю, что должно быть возможно определить макрос препроцессора C, который принимает строку и размер строки в качестве аргументов и возвращает последовательность, состоящую из символов в строке (с использованием строкового BOOST_PP_FOR
преобразования, индексов массива и т. Д.). Однако у меня нет времени (или достаточно интереса) для реализации такого макроса =)
constexpr
функциях и инициализировать массивы (следовательно, concat, substr и т. Д.).
constexpr
строки можно анализировать во время компиляции, так что вы можете использовать разные пути кода в зависимости от результатов. По сути, вы можете создавать EDL в C ++; приложения довольно безграничны.