Możliwość tworzenia i manipulowania napisami podczas kompilacji w C ++ ma kilka przydatnych aplikacji. Chociaż możliwe jest tworzenie ciągów znaków czasu kompilacji w C ++, proces ten jest bardzo uciążliwy, ponieważ ciąg musi być zadeklarowany jako zmienna sekwencja znaków, np.
using str = sequence<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!'>;
Operacje, takie jak konkatenacja ciągów, wyodrębnianie podciągów i wiele innych, można łatwo zaimplementować jako operacje na sekwencjach znaków. Czy można wygodniej deklarować ciągi czasu kompilacji? Jeśli nie, to czy w pracach jest propozycja, która pozwoliłaby na wygodne deklarowanie ciągów czasu kompilacji?
Dlaczego istniejące podejścia zawodzą
Idealnie chcielibyśmy móc zadeklarować ciągi czasu kompilacji w następujący sposób:
// Approach 1
using str1 = sequence<"Hello, world!">;
lub używając literałów zdefiniowanych przez użytkownika,
// Approach 2
constexpr auto str2 = "Hello, world!"_s;
gdzie decltype(str2)
miałby constexpr
konstruktora. Bardziej chaotyczna wersja podejścia 1 jest możliwa do wdrożenia, wykorzystując fakt, że możesz wykonać następujące czynności:
template <unsigned Size, const char Array[Size]>
struct foo;
Jednak tablica musiałaby mieć zewnętrzne połączenie, więc aby podejście 1 działało, musielibyśmy napisać coś takiego:
/* Implementation of array to sequence goes here. */
constexpr const char str[] = "Hello, world!";
int main()
{
using s = string<13, str>;
return 0;
}
Nie trzeba dodawać, że jest to bardzo niewygodne. Podejście 2 w rzeczywistości nie jest możliwe do wdrożenia. Gdybyśmy mieli zadeklarować constexpr
operator literału ( ), to w jaki sposób określilibyśmy zwracany typ? Ponieważ potrzebujemy operatora, aby zwracał zmienną sekwencję znaków, musielibyśmy więc użyć const char*
parametru do określenia typu zwracanego:
constexpr auto
operator"" _s(const char* s, size_t n) -> /* Some metafunction using `s` */
Powoduje to błąd kompilacji, ponieważ s
nie jest constexpr
. Próba obejścia tego problemu poprzez wykonanie poniższych czynności niewiele pomaga.
template <char... Ts>
constexpr sequence<Ts...> operator"" _s() { return {}; }
Standard mówi, że ten konkretny operator literału jest zarezerwowany dla typów całkowitych i zmiennoprzecinkowych. Chociaż 123_s
zadziała, abc_s
nie zadziała . A co, jeśli całkowicie porzucimy literały zdefiniowane przez użytkownika i po prostu użyjemy zwykłej constexpr
funkcji?
template <unsigned Size>
constexpr auto
string(const char (&array)[Size]) -> /* Some metafunction using `array` */
Tak jak poprzednio, napotykamy na problem polegający na tym, że tablica, teraz parametr constexpr
funkcji, sama w sobie nie jest już constexpr
typem.
Uważam, że powinno być możliwe zdefiniowanie makra preprocesora C, które pobiera ciąg i rozmiar ciągu jako argumenty i zwraca sekwencję składającą się ze znaków w ciągu (używając BOOST_PP_FOR
, stringifikacji, indeksów tablicy i tym podobnych). Nie mam jednak czasu (lub wystarczającego zainteresowania) na zaimplementowanie takiego makra =)