前回、ユーザー定義リテラルのすべてを説明したつもりであったが、いくつか細かい漏れがあった。
まず、ユーザー定義リテラルの宣言であるが、実は文法上、空白文字が必要な箇所がある。""と識別子の間だ
int operator "" /*ここに少なくともひとつの空白文字が必要*/ _identifier( unsigned long long int ) ;
空白文字を入れないと、""_identiferがひとつのトークンだとみなされてしまう。
もちろん、この宣言を参照する際にも、空白文字が必要である。
operator "" /*空白文字が必要*/ _identifer( 123ULL ) ;
また、ユーザー定義リテラルのオーバーロードは、あらかじめ定められた仮引数リストやテンプレートしか使えないが、その他は、普通の関数と変わりないということである。もちろん、inlineやconstexprで宣言することもできる。当然、内部リンケージを持つ可能性もある。
inilne unsigned long long int operator "" _inline_udl( unsigned long long int value ) { return value ; } constexpr unsigned long long int operator "" _constexpr_udl( unsigned long long int value ) { return value ; }
もちろん、アドレスだって取得できる。
unsigned long long int operator "" _udl( unsigned long long int value ) { return value ; } int main() { using udl_pointer_type = unsigned long long int (*)( unsigned long long int ) ; // アドレスの取得 udl_pointer_type p = &operator "" _udl ; }
また、当然ながら、名前空間スコープ内に宣言される。名前解決も通常通りである。
namespace foo { unsigned long long int operator "" _udl( unsigned long long int value ) { return value ; } } int main() { 123_udl ; // エラー { using namespace foo ; 123_udl ; // OK } { using foo::operator "" _udl ; 123_udl ; // OK } }
もちろん、オーバーロード解決も通常通りである。
void operator "" _udl( unsigned long long int value ) { } // #1 void operator "" _udl( long double value ) { } // #2 int main() { operator "" _udl( 123ULL ) ; // #1 operator "" _udl( 1.23L ) ; // #2 }
また、文字列やテンプレート実引数で受け取る場合のオーバーロードの優先順位は、
- 整数リテラルの場合、仮引数unsigned long long int、浮動小数点数リテラルの場合、long double
- それが見つからない場合、char const *
- それが見つからない場合、テンプレート実引数
となっている。したがって、
void operator "" _u1( unsigned long long int value ) { } // #1 void operator "" _u1( char const * ) { } // #2 void operator "" _u2( char const * ) { } // #3 template < char ... Types > void operator "" _u2( ) { } // #4 int main() { 123_u1 ; // #1 123_u2 ; // #3 }
このようにオーバーロード解決される。
つまり、いくつかの制限と例外的なルールを除けば、およそ関数に適用されるルールは、ユーザー定義リテラルのオーバーロードにも適用される。
No comments:
Post a Comment