前回、ユーザー定義リテラルのすべてを説明したつもりであったが、いくつか細かい漏れがあった。
まず、ユーザー定義リテラルの宣言であるが、実は文法上、空白文字が必要な箇所がある。""と識別子の間だ
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