C ++ 11のカスタムリテラル

C ++ 11標準の採用から6か月以上が経過しました。 ネットワークには、新しい標準専用の多くの資料がありますが、それらのほとんどは最も単純な機能に関連しており、最も甘いものです。 ラムダ関数、自動型推論システム、新しい指定子、スマートポインターなどについて話しています。 はい、これらは非常に興味深いものであり、安全であると言うことができる、それらは最も有用で頻繁に使用されるものの一つです。 しかし、光はそれらに収束せず、新しいC ++ 11はそれらだけではありません。



以下では、カスタムリテラルについて説明します。これは非常に便利なツールですが、日常的な目的ではありません。





リテラルとは何ですか?



リテラルは、オブジェクトを作成する式です。 リテラルはC ++ 11だけでなく、C ++ 03にもありました。 たとえば、文字、文字列、実数などを作成するためのリテラルがあります。



'x'; // character "some"; // c-style string 7.2f; // float 74u; // unsigned int 74l; // long 0xF8; // hexadecimal number
      
      





これらはすべてリテラルです。 リテラルの概念で、私たちはそれを理解したと思います。 C ++ 11に戻りましょう。



C ++ 11のカスタムリテラル



上記のように、新しい標準はカスタムリテラルを作成するためのツールを提供します。 ユーザーリテラルには、 生のリテラル組み込み型(調理済み)のリテラルの2つのカテゴリがあります。



ただし、C ++では接尾辞リテラルのみを作成できることに注意してください。 つまり、リテラルプレフィックス( 0xなど)またはプレフィックスサフィックス( ""など )の作成は失敗します。



数値型のリテラル


組み込み型のリテラルから始めましょう。 数値型のリテラルを作成するには、次の2つの署名のいずれかを使用する必要があります。



  //      OutputType operator "" _suffix(unsigned long long); //      OutputType operator "" _suffix(long double);
      
      





リテラルの使用法は次のとおりです。



  42_suffix; // OutputType operator "" _suffix(unsigned long long); 42.24_suffix; // OutputType operator "" _suffix(long double);
      
      







署名に注意してください。



これらのタイプは理由のために使用され、他のものと置き換えることはできません。 これらは必須であり、言語標準によって承認されています。



以下は、分を秒に変換するリテラルの例です。



  unsigned long long operator "" _min(unsigned long long minutes) { return minutes * 60; } // ... std::cout << 5_min << std::endl; //    300
      
      







文字列型のリテラル


このタイプのリテラルを作成するには、次の署名のいずれかを使用する必要があります。



  OutputType operator "" _suffix(const char* str, size_t size); OutputType operator "" _suffix(const wchar_t* str, size_t size); OutputType operator "" _suffix(const char16_t* str, size_t size); OutputType operator "" _suffix(const char32_t* str, size_t size);
      
      





署名は、文字列のタイプに応じて選択されます。



  "1234"_suffix; // operator "" _suffix(const char* str, size_t size); u8"1234"_suffix; // operator "" _suffix(const char* str, size_t size); L"1234"_suffix; // operator "" _suffix(const wchar_t* str, size_t size); u"1234"_suffix; // operator "" _suffix(const char16_t* str, size_t size); U"1234"_suffix; // operator "" _suffix(const char32_t* str, size_t size);
      
      





Cスタイルの文字列をstd::string



列に変換するリテラルの例を以下に示します。



  std::string operator "" s(const char* str, size_t size) { return std::string(str, size); } // ... std::cout << "some string"s.length() << std::endl;
      
      







生のリテラル


さて、最後に、生のリテラルの時間です。 生のリテラル署名は次のとおりです。



  OutputType operator "" _suffix(const char* literalString);
      
      





このタイプのリテラルは、入力番号を文字ごとに解析する必要がある場合に役立ちます。 T..e。 この場合、数値は文字列として演算子に渡されます。 完全に明確でない場合は、以下のコードをご覧ください。



  OutputType operator "" _x(unsigned long long); OutputType operator "" _y(const char*); 1234_x; // call: operator "" _x(1234); 1234_y; // call: operator "" _y("1234");
      
      





このタイプのリテラルを使用すると、2進数を10進数に変換するリテラルを作成できます。 たとえば、次のように:



  unsigned long long operator "" _b(const char* str) { unsigned long long result = 0; size_t size = strlen(str); for (size_t i = 0; i < size; ++i) { assert(str[i] == '1' || str[i] == '0'); result |= (str[i] - '0') << (size - i - 1); } return result; } // ... std::cout << 101100_b << std::endl; //  44
      
      





生のリテラルには別のシグネチャがあります。 Variadic Templateのアプリケーションに基づいています:



  template <char...> OutputType operator "" _b();
      
      





Variadic Templateベースのリテラルの利点は、コンパイル時に計算できることです。 同じ2進数から10進数へのリテラルは、次のように書き換えることができます。



  template <char... bits> struct to_binary; template <char high_bit, char... bits> struct to_binary<high_bit, bits...> { static_assert(high_bit == '0' || high_bit == '1', "Not a binary value!"); static const unsigned long long value = (high_bit - '0') << (sizeof...(bits)) | to_binary<bits...>::value; }; template <char high_bit> struct to_binary<high_bit> { static_assert(high_bit == '0' || high_bit == '1', "Not a binary value!"); static const unsigned long long value = (high_bit - '0'); }; template <char... bits> constexpr unsigned long long operator "" _b() { return to_binary<bits...>::value; } // ... int arr[1010_b]; //      std::cout << 101100_b << std::endl; //  44
      
      





注意深い読者は、 「生のリテラルと同じ名前の数字のリテラルの両方を作成したらどうなるでしょうか? コンパイラはどのリテラルを使用しますか?」 この問題の標準は正確な答えを提供し、コンパイラが次の順序でリテラルを使用しようとしていることを説明しています。





ユーザー定義のリテラルがシステムリテラル(たとえば、 f )と一致する場合、システムリテラルが実行されることを知っておくと便利です。



  long operator "" f(long double value) { return long(value); } // ... std::cout << 42.7f << std::endl; //  42.7
      
      







結論



Going Native 2012の BjörnStraustrupは、リテラルを使用する便利な例を示しました。 私は、コードの可読性を高めるという事実を明確に実証し、間違いの可能性も減らすように思えます。



  Speed sp1 = 100m / 9.8s; // very fast for a human Speed sp2 = 100m / 9.8s2; // error (m/s2 is acceleration) Speed sp3 = 100 / 9.8s; // error (speed is m/s and 100 has no unit)
      
      







ユーザーリテラルメカニズムは、場合によっては便利なツールです。 どこでも使用する価値はありません。 リテラルは潜行性があるため、使用する前によく考えてください。







PS:

カスタムリテラルは、 gcc 4.7およびclang 3.1コンパイラでサポートされています。



All Articles