🚫

コンパイル時文字列

2024/01/21に公開

コンパイル文字列

実装

コンパイル時に計算可能な文字列を表す文字列

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)

https://github.com/gen740/Argo.git

GitHubで編集を提案

Discussion