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 {}; }
標準では、この特定のリテラル演算子形式は整数型と浮動小数点型用に予約されていると規定されています。while は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
、文字列化、配列の添え字などを使用)。ただし、そのようなマクロを実装する時間 (または十分な関心) はありません =)
ベストアンサー1
これほど優雅なものは見たことがないスコット・シュアのstr_const
発表されたC++ ナウ 2012constexpr
ただし、必要です。
使い方と機能は次のとおりです:
int
main()
{
constexpr str_const my_string = "Hello, world!";
static_assert(my_string.size() == 13, "");
static_assert(my_string[4] == 'o', "");
constexpr str_const my_other_string = my_string;
static_assert(my_string == my_other_string, "");
constexpr str_const world(my_string, 7, 5);
static_assert(world == "world", "");
// constexpr char x = world[5]; // Does not compile because index is out of range!
}
コンパイル時の範囲チェックほどクールなものはありません!
使用と実装の両方にマクロは使用されていません。また、文字列のサイズに人為的な制限はありません。実装をここに投稿したいところですが、Scott の暗黙の著作権を尊重しています。実装は、上にリンクされている彼のプレゼンテーションの 1 つのスライドにあります。
C++17 のアップデート
この回答を投稿してから数年経ち、std::string_view
は私たちのツールボックスの一部になりました。 以下は、 を使用して上記を書き直す方法ですstring_view
。
#include <string_view>
int
main()
{
constexpr std::string_view my_string = "Hello, world!";
static_assert(my_string.size() == 13);
static_assert(my_string[4] == 'o');
constexpr std::string_view my_other_string = my_string;
static_assert(my_string == my_other_string);
constexpr std::string_view world(my_string.substr(7, 5));
static_assert(world == "world");
// constexpr char x = world.at(5); // Does not compile because index is out of range!
}