2009-11-25

rvalue reference 補足

前回、rvalue reference 完全解説なる記事を書いた所、ある程度の反響を得た。このような個人のブログでは、数人が思う所を書けば、まあ、ある程度の反響と言って差し支えないのである。

しかるに、皆rvalue referenceの理解に苦しんでいる様子だ。果たしてあの解説が至らなかったのであろうか。しかし、あの解説は、これ以上ないくらい簡単に書いたはずである。

実際、value referenceは、名前の通りのrvalueへのreferenceに過ぎぬのだし、std::move()とstd::forward()は、キャストに過ぎぬのだ。std::move()は、rvalueへのキャストであり。std::forward()は、lvalueをlvalueへ、rvalueをrvalueへキャストするのである。それだけのことなのである。

しかし、現実に理解に苦しんでいる者がいる。これは一体どういう事なのか。つくづくこの事を案ずるに、これは畢竟、rvalue referenceが理解できないのではなく、C++のその他の規格を理解していないのではないかと思う。

lvalueとrvalue

どうやら、lvalueとrvalueの違いを理解していないC++プログラマがいるらしい。C++98の頃から、lvalueとrvalueはある。これを理解せずには、C++98すら使いこなせないのではないかと思うのだが、不思議なものだ。

思うに、lvalueとrvalueというのは、名前がよろしくない。もはや、left, rightとは何の関係もないのだ。従って、もしC++の本で、左辺値、右辺値、などという訳語を使っている本があれば、例外なくクソであることが予想されるので買わない方が良い。

named valueとunnamed valueの方が分かりやすいという意見もある。確かに、rvalueに名前はない。また、rvalueは一時的なオブジェクトである。

templateのargument deduction

ある者は、std::forward()を、テンプレートではない普通の関数の中もで使うべきかどうか疑問に思っている。本来、こんな疑問が生ずること自体おかしいのだ。std::forward()はargument deductionに関係する問題なのだから。

そしてふと思ったのだが、もしや、世の中にはargument deductionを知らないC++プログラマがいるのではあるまいかということだ。一体どうやってテンプレート関数を書いているのか知らないが、どうやらいるらしい。不思議なことだ。

argument deductionとは、テンプレート引数を関数の引数として使った場合、テンプレート引数を指定しなくても、型を推定してくれる機能のことだ。

template < typename T > void f ( T ) {}
template < typename T > void g ( T * ) {}
template < typename T > void h ( T const * ) {}


int main()
{
    int const * ptr = nullptr ;

    f( ptr ) ; // T は int const *
    g( ptr ) ; // T は int const
    h( ptr ) ; // T は int
}

もしこの機能がないならば、プログラマは非常なる不便を被るであろう。

そのargument deductionとして、rvalue referenceがlvalueになるのである。

template < typename T > void f ( T && )
{
    std::cout<< std::is_lvalue_reference<T>::value << std::endl ;
}

int main()
{
    int x = 0 ;

    // T は int &
    f( x ) ;

    // T は int
    f( std::move(x) ) ;
}

つまり、テンプレート引数で、関数の引数をrvalue referenceにすると、引数がlvalueかrvalueか、インスタンス化されるまで分からないのである。

だから、std::moveは使えないのである。うっかり引数のlvalue referenceをrvalue referenceにしてしまおうものなら、呼び出し元が悲惨なことになるだろう。したがって、lvalueはlvalueのままにしておいてくれる、特別なキャストが必要なのである。

No comments: