【C++11 Advent Calendar 2011】ユーザ定義リテラル【1日目】
これは C++11 Advent Calendar 2011 の1日目の記事です。
1日目の C++11 Advent Calendar 2011 は『ユーザ定義リテラル』ついて書きたいと思います。
出落ちなのは気のせいです。
もし間違っている記述があれば、ご指摘頂けると助かります。
[C++11 Advent Calendar 2011について]
たくさんの方にご参加いただきありがとうございます。
なんとか12月1日までに25人埋めることが出来ました。
Advent Calendar は25日までですが、それ以降に参加されても全然問題ありません。
2週目をやりたい!って方も、この記事のコメントか @manga_osyo までリプくだされば、スケジュール表に追記します。
まだまだ参加される方をお待ちしております!
[C++11 に対応しているコンパイラ]
まだ、C++11 に完全に対応しているコンパイラは存在しません。
各コンパイラによって対応している機能が違っているので、手元のコンパイラと照らしあわせて確認して下さい。
今回ネタにする『ユーザ定義リテラル』は、gcc 4.7 でのみ対応しています。
Windows であれば、こちらからバイナリをダウンロードしてきて使用することが出来ます。
[ユーザ定義リテラル]
C++ では、整数や小数、文字列のリテラルを定義する際に修飾子を付けて型定義する事が出来ます。
int n1 = 42; // int 型 unsigned int n2 = 42u; // unsigned int 型 double n3 = 3.14; // double 型 float n4 = 3.14f; // float 型
C++11 では、u や f などをユーザで定義する事が出来るようになりました。
[書き方]
ユーザ定義リテラルは operator "" をキーワードにして定義します。
OutputType operator "" _suffix(unsigned long long); OutputType v = 1234_suffix; // operator "" _suffix(1234);
"" と _suffix の間にはスペースが必要なことに注意して下さい。
[ユーザ定義リテラルの名前]
アンダースコアで始まらないサフィックスは標準で予約されているので、アンダースコアから始める必要があります。
(アンダースコアから始まらなくてもコンパイルは通りますが、将来のことを考えて止めておきましょう。
OutputType operator "" _suffix(...); // Good OutputType operator "" suffix(...); // Bad
[文字列リテラル]
文字列リテラルの場合は、文字型と文字列の長さを受け取ります。
char const* operator "" _suffix(char const* str, std::size_t len){ return str; } wchar_t const* operator "" _suffix(wchar_t const* str, std::size_t len){ return str; } char16_t const* operator "" _suffix(char16_t const* str, std::size_t len){ return str; } char32_t const* operator "" _suffix(char32_t const* str, std::size_t len){ return str; } char const* str1 = "hoge"_suffix; // char const* char const* str2 = u8"hoge"_suffix; // char const* wchar_t const* str3 = L"hoge"_suffix; // wchar_t const* char16_t const* str4 = u"hoge"_suffix; // char16_t const* char32_t const* str5 = U"hoge"_suffix; // char32_t const*
char16_t と char32_t は C++11 で追加された文字型です。
同様に u8""、u""、U"" といった書き方も C++11 で追加された文字列リテラルです。
[整数と小数]
整数と小数のリテラルの場合は、いくつか書き方があります。
1.それぞれの型で受け取る
unsigned long long と long double で受け取ります。
unsigned long long operator "" _suffix(unsigned long long n){ return n; } long double operator "" _suffix(long double d){ return d; } unsigned long long n = 42_suffix; // operator "" _suffix(42); long double d = 3.14_suffix; // operator "" _suffix(3.14);
2.char const* で受け取る
文字列(char const*)として受け取ります。
char const* operator "" _suffix(char const* p){ return p; } char const* p = 42_suffix; // operator "" _suffix("42"); char const* p = 3.14_suffix; // operator "" _suffix("3.14");
小数の場合は、ピリオドも受け取ることに注意して下さい。
文字列リテラルとは違い、文字列の長さは引数で受け取りません。
3.template で受け取る
整数と小数は、char 型の可変長テンプレート引数として受け取ることが出来ます。
template<char ...cs> std::size_t operator "" _suffix(){ return sizeof...(cs); } std::size_t len1 = 42_suffix; // operator "" _suffix<'4', '2'>(); std::size_t len2 = 3.14_suffix; // operator "" _suffix<'3', '.', '1', '4'>(); assert(len1 == 2); assert(len2 == 4);
このようにリテラルの各文字をテンプレート引数として受け取ります。
char const* と同様に小数の場合は、ピリオドも文字として受け取ることに注意して下さい。
これを利用して、リテラルによって型を変えることも出来たりします。
[符号(+と-)]
符号(+ や -)は ユーザ定義リテラルの引数に渡されずに、リテラルが評価された後で付属されます。
char const* operator "" _suffix(char const* p){ return p; } // error: wrong type argument to unary minus -42_suffix; // - operator "" _suffix("42")
符号を付ける必要があるリテラルは、戻り値型に operator - を定義するか、文字列リテラル("-42"_suffix)などを使用する必要があります。
[リテラル値をテンプレート引数に渡す]
整数や小数のリテラルは、template
namespace detail{ template<std::size_t acc, char ...cs> struct atoi_impl : std::integral_constant<std::size_t, acc>{}; template<std::size_t acc, char c, char ...cs> struct atoi_impl<acc, c, cs...> : atoi_impl<(acc * 10) + (c - '0'), cs...>{}; template<char ...cs> struct atoi : atoi_impl<0, cs...>{}; } // namespace detail // プレースホルダーをユーザ定義リテラルで定義する typedef std::_Placeholder<1> _1; template<char ...cs> std::_Placeholder<detail::atoi<cs...>::value> operator "" _arg(){ return {}; } assert(std::bind(std::plus<int>(), 1_arg, 2_arg)(1, 3) == 4); assert(std::bind(std::minus<int>(), 1_arg, 3_arg)(1, 3, 2) == -1);
[多倍長整数型を定義]
char const* 型を受け取る多倍長整数型の場合は、ユーザ定義リテラルを使用して定義する事も出来ます。
// char const* を受け取る多倍長整数型 bigint make_bigint(char const*); bigint operator "" _bigint(char const* str){ return make_bigint(str); } auto x = 9148108038107407105720570_bigint; // auto x = make_bigint("9148108038107407105720570");
文字列リテラルで定義するよりも見やすいのではないでしょうか。
[単位のシンタックスシュガーとして扱う]
単位をユーザ定義リテラルで定義します。
namespace SI{ std::chrono::seconds operator "" _seconds(unsigned long long n){ return std::chrono::seconds{n}; } std::chrono::minutes operator "" _minutes(unsigned long long n){ return std::chrono::minutes{n}; } std::chrono::hours operator "" _hours(unsigned long long n){ return std::chrono::hours{n}; } } // namespace SI using namespace SI; auto x = 1_hours + 5_minutes + 30_seconds; // こっちの記述よりも見やすい気がする // auto x = hours(1) + minutes(5) + seconds(30);
ユーザ定義リテラルはこういう使われ方が多くなると思います。
[まとめ]
実際のところユーザ定義リテラルは単なるシンタックスシュガーでしかありません。
しかし、整数や小数のリテラル値をテンプレート引数に渡せるのはユーザ定義リテラルの強みです。
今後、どんな普通に使われていくのかが楽しみです。