🚫
コンパイル時文字列
コンパイル文字列
実装
コンパイル時に計算可能な文字列を表す文字列
template <size_t N>
struct String {
char str_[N] = {};
String() = default;
consteval String(const char (&str)[N + 1]) {
for (size_t i = 0; i < N; i++) {
str_[i] = str[i];
}
};
constexpr explicit operator std::string() const {
return std::string(str_, N);
}
constexpr explicit operator std::string_view() const {
return std::string_view(str_, N);
}
[[nodiscard]] consteval auto operator[](size_t n) const {
return str_[n];
}
consteval auto operator[](size_t n) -> char& {
return str_[n];
}
};
template <size_t N>
String(const char (&)[N]) -> String<N - 1>;
前提状況
- C++ ではコンパイル時に
const char *
を宣言することは可能
consteval const char* str = "Hello, World"
- しかしこれを非型テンプレートパラメータに入れることはできない
constexpr const char c = 'c';
constexpr const char* c_ptr = &c;
constexpr const char* d_ptr = "Hello, World!";
template <auto T>
struct A {};
A<c_ptr>(); // OK c_ptr は literal 型
A<d_ptr>(); // Error d_ptr は 非literal 型
- とんでもな状況だが C++ では string literal は literal 型ではなく従ってテンプレートパラメータに入れることはできない
- その上
std::string
も内部で string literal を保有しているためstd::string
を非型テンプレートとして用いることはできない - 一方で
char[]
のような配列型は、コンパイル時に要素が定まっている限り literal 型となる
constexpr char[5] = {'H', 'e', 'l', 'l', 'o'};
- C++20 からは constexpr 関数等の制限が緩み string literal が constexpr 関数内で使用できるようになった
- string literal から literal 型である配列型にコンパイル時に変更できれば、string literal を literal 型のように扱うことが可能になる
実装の説明
- まずは literal 型となる値の storage を考える
- その型が literal 型であるためには
- 配列のサイズがコンパイル時に決まる
-
private
であるメンバー変数を持ってはいけない
template <size_t N>
struct String {
char str_[N] = {};
};
- これをベースで
cosnt char*
をstr_
に保持する実装をする - その上でコンパイラーが string literal を
const char[]
に推論しString
に格納されるようにする
template <size_t N>
String(const char (&)[N]) -> String<N - 1>;
使用例
- これを使うと string literal をテンプレートパラメータに格納可能になる。(使用例)
template <String str>
struct A {
void print() { std::println("{}", std::string(str)); }
};
int main() {
auto a = A<"Hello, world!">();
a.print();
}
- これを用いたコンパイル時決定の CLI パーサーも実装した(→ Compiler Exploler)
Discussion