注意:この記事は、現行のドラフトの先を見据えて書いている。この記事を読む前に、core issue 535と、core issue 1080を参照されたし。
次のFDISで大幅な変更が来るので、現行のN3225が一切信用できない。そこでこの一ヶ月は、執筆を急ぐより、規格のテンプレートとオーバーロードの文面をじっくり読む期間にあてることにする。
テンプレートとオーバーロードと暗黙の特別なメンバー関数が関係する場合、非常に分かりにくい挙動になる。もちろん、規格を厳密に解釈すれば、何も恐れることはないのだが、それでも、やはり難しい場合は存在する。
たとえば以下のコードだ。
struct X
{
X() { }
template < typename T >
X( T const & ) ;
template < typename T >
X & operator = ( T const & ) ;
} ;
int main()
{
X a ;
X b(a) ; // use implicit copy constructor
b = a ; // use implicit copy assignment operator
}
テンプレートコンストラクターとテンプレート代入演算子は、コピーコンストラクターやコピー代入演算子ではない。ただし、コピーコンストラクターやコピー代入演算子と同じ方法で呼び出されるし、オーバーロード解決の候補にも上がる。
ただし、テンプレートコンストラクターはコピーコンストラクターではないということは、暗黙のコピーコンストラクターの生成を妨げないということである。つまり、上記のコードでは、暗黙のコピーコンストラクターは、依然として生成される。オーバーロード解決で、型の順位が同じであった場合、非テンプレート関数はテンプレート関数より良しとされるので、非テンプレート関数である、暗黙のコピーコンストラクターが選ばれる。代入演算子も同じ。
したがって、上記の場合で、テンプレート版のコンストラクターや代入演算子を使いたい場合は、暗黙の特別なメンバー関数を、deleted定義しなければならない。
struct X
{
X() { }
X( X const & ) = delete ;
template < typename T >
X( T const & ) ;
X & operator = ( X const & ) = delete ;
template < typename T >
X & operator = ( T const & ) ;
} ;
int main()
{
X a ;
X b(a) ; // use template constructor
b = a ; // use template assignment operator
}
では、以下の場合はどうだろうか。
struct X
{
X() { }
template < typename T >
X( T && ) ;
template < typename T >
X & operator = ( T && ) ;
} ;
int main()
{
X a ;
X b(a) ; // use template constructor
b = a ; // use template assignment operator
}
この場合、テンプレート版のコンストラクターや代入演算子を使う。何故だろうか。これには、非常にややこしいわけがある。
テンプレート仮引数に対するrvalueリファレンスが仮引数で、実引数がlvalueの場合、テンプレート仮引数はlvalueリファレンスとなり、rvalueリファレンスは無視されるというルールがある。
template < typename T >
void f( T && ) ;
int main()
{
int x = 0 ;
f( x ) ; // f<int &>(int &)
}
これと同じことが、テンプレートのコンストラクターや代入演算子でも起こっている。ではなぜ、非テンプレートよりテンプレートの方が優先されるのか。それは、const修飾子を付け加えるというのも、標準型変換の一種だからだ。テンプレート版は、型を一切変換する必要がないので当然、非テンプレート版より順位が高い。そのため、テンプレート版が選ばれる。
とにかく、現行のN3225は一切信用できない。はやく3月末になってほしいものだ。
2 comments:
1080で
uses the implicitly <del>generated</del> declared copy constructor
とあるけど
declared でなく defined で
uses the implicitly <del>generated</del> defined copy constructor
では。
宣言で問題ないでしょう。
12.8 [class.copy] paragraph 8にも、
a copy constructor is implicitly declared as defaulted (8.4.2).
とあります。
Post a Comment