Возможность создавать строки и манипулировать ими во время компиляции в 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
литеральный оператор (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
пользовательские литералы и просто воспользуемся обычной функцией constexpr
?
template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using 'array' */
Как и прежде, мы сталкиваемся с проблемой, что массив, теперь являющийся параметром функции constexpr
, сам по себе больше не является типом constexpr
.
Я считаю, что должна быть возможность определить макрос препроцессора C, который принимает строку и размер строки в качестве аргументов и возвращает последовательность, состоящую из символов в строке (используя BOOST_PP_FOR
, BOOST_PP_FOR
, индексы массива и тому подобное). Однако у меня нет времени (или достаточного интереса) для реализации такого макроса =)