パスワードを忘れた? アカウント作成
99779 journal

eldeshの日記: 右辺値参照2/N(typo修正)

日記 by eldesh
右辺値参照続き.

perfect fowarding

[左右]辺値参照とconstの有無でオーバーロードした関数に,
引数を転送するテンプレート関数を書きたいときに使用する転送テンプレート関数の書き方.

void hoge( string & );
void hoge( string const & );
void hoge( string && );
void hoge( string const && );

template< typename T >
void trans_hoge( T&& t ){
    hoge(t);
}

string str("hoge");        // 左辺値
const string cstr("foo");    // constな左辺値
string bar(void){
    return "bar";
}
string const bazz(void){
    return "bazz";
}

trans_hoge(str);    // hoge(string&)
trans_hoge(cstr);    // hoge(string const&)
trans_hoge(bar());    // hoge(string&)    !!
trans_hoge(bazz());    // hoge(string const&)    !!

となって,左辺値参照Ver.が呼ばれてしまう.
なぜなら,hoge(t) tという名前が付いてるから(右辺値ではない)
これを回避するためには再度,名前を無くせば良い.

template< typename A >
struct Identity {            // id a = a
    typedef A type;
};
template< typename T >
T&& Forward( Identity<T>::type&& t ){    // reference collapsing 回避
    return(t);
}
template< typename T >
void trans_hoge( T&& t ){
    // 名前が無くなる
    hoge(Forward<T>(t));        // 必ず<T>を明示
}
trans_hoge(bar());    // hoge(string&&)    計画通り!!
trans_hoge(bazz());    // hoge(string const&&)    計画通り!!

Identity::typeに無理やり&&を付与しているので, reference collapsingが働かない.(なんで働かないのか厳密にはよく分からないorz)
ここで,通常の推論に任せると,trans_hogeの引数tの型はT&となるので,
Identity::type&&が参照の参照型となりコンパイルエラーとなってしまう.
これを回避するために,Forwardのパラメータ型は明示する必要がある.

Move semantics

右辺値参照の真髄(だと思う). 右辺値参照の参照先からリソースの所有権(つーかポインタ)を強奪する.
以下のような感じで使う.

class C {
public:
    int *m_p;
    C(int x) : x_(new int(x))        // a
    {}

    C( C&  t ) : x_(new int(*t.m_p))    // b
    {]

    C( C&& t ) {                // c
        m_p = *t.m_p;
        t.m_p = NULL;
    }
};

class wrapperC {
    C m_c;
public:
    wrapperC(int x) : m_c(x){}
    wrapperC( C&& c ) : m_c(Move(c)) {}
    wrapperC( C& c ) : m_c(Move(c)) {}
    wrapperC( wrapperC&& c ) : m_c(Move(c)) {}
};

C createC(int x){
    C c(x);
    return c;
}
wrapperC c0(5);            // メソッドaが使われる
wrapperC c1(c0);        // メソッドbが(ry
wrapperC c2( createC(6) );    // メソッドc(ry
wrapperC c2 = createC(7);    // c(ry

03ではcreateC()でインスタンスが作られ,C(C&)でコピーされてから破棄されるが,
右辺値参照は,そこで破棄されるはずの一時変数に言及できるので, ポインタ(メンバ)を横取りするだけでinstantiateが完了する.(代入も同じ)
さらにコンストラクタにMoveを書くだけで,
右辺値が来たときは勝手にMoveSemanticsが発動(?)し, 左辺値が来たときはコピーが動く!

とりあえずここまでで終了.(題名のNは2ということで)
だいたい理解したと思う.

まとめ:右辺値でおk

# 疲れた。

Mon Jul 6 14:29:58 2009 - wrapperCクラスのコンストラクタ名を修正.ひどいtypoだった….

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
typodupeerror

吾輩はリファレンスである。名前はまだ無い -- perlの中の人

読み込み中...