1

次のコードを直接持っています: http://www.justsoftwaresolutions.co.uk/cplusplus/rvalue_references_and_perfect_forwarding.html

g++ 4.8.1 で次のようにコンパイル: g++ -std=c++11 testforward.cpp -o testforward.exe

#include <cstdlib>
#include <vector> 
#include <string> 
#include <iostream>   
#include <algorithm> 

class X
{
    std::vector<double> data;
public:
    X():
        data(100000) // lots of data
    {}
    X(X const& other): // copy constructor
        data(other.data)   // duplicate all that data
    {}
    X(X&& other):  // move constructor
        data(std::move(other.data)) // move the data: no copies
    {}

    X& operator=(X const& other) // copy-assignment
    {
        data=other.data; // copy all the data
        return *this;
    }

    X& operator=(X && other) // move-assignment
    {
        data=std::move(other.data); // move the data: no copies
        return *this;
    }
};



void g(X&& t)
{
    std::cout << "t in g is rvalue" << std::endl ;
}
void g(X& t)
{
    std::cout << "t in g is lvalue" << std::endl ;
}

template<typename T>
void f(T&&t)
{
    g(std::forward<T>(t)) ;
}

void h(X &&t)
{
    g(t) ;
}


int main()
{
    X x;
    f(x);   // 1
    f(X()); // 2
    //h(x);  //compile error 
    h(X()); // 3
}

著者によると、以下に説明します。

右辺値参照を関数テンプレートと組み合わせると、興味深い相互作用が得られます。関数パラメーターの型がテンプレート型パラメーターへの右辺値参照である場合、左辺値が渡された場合、型パラメーターは左辺値参照であると推定されます。それ以外の場合は入力...

このテストの結果出力は次のとおりです。

t in g is lvalue
t in g is rvalue
t in g is lvalue

f(x) get "t in g is lvalue" は期待どおりです !!

f(X()) get "t in g is rvalue" 、はい、それが std::forward に使用されたものです

h(X()) get "t in g is lvalue" 、これは私の質問です。関数 h はテンプレート関数ではないことがわかります。著者が説明しているように、「右辺値参照を関数テンプレートと組み合わせると、興味深い結果が得られます。相互作用」はそうではありませんが、それでもこの関数出力「t in g is lvalue」は、この興味深い相互作用がテンプレート関数だけでなく、通常の関数にも発生することを意味します!!

コードを次のように変更すると:

void h(X &&t)
{
    g(std::forward<X>(t)) ;
}

「t in g is rvalue」が得られます!!!

テストによると、作者は「右辺値参照を関数テンプレートと組み合わせると、興味深い相互作用が得られる」と説明していると言いましたが、実際にはテンプレート関数だけでなく、通常の関数にも適用されます。または、私の英語が苦手なので、できますこの説明を聞き取れませんよね?!

編集 :

void h(X &&t)
{
    g(t) ;
}

void h(X &t)
{
    g(t) ;
}

h(x);      //get "t in g is lvalue"
h(X());    //get "t in g is lvalue"

=====================================================

void h(X &&t)
{
    g(std::forward<X>(t)) ;
}

void h(X &t)
{
    g(std::forward<X>(t)) ;
}

h(x);      //get "t in g is rvalue"
h(X());    //get "t in g is rvalue"

テンプレート関数のみのように見えますが、std::forward の cprrect 使用法を取得します !!!

4

1 に答える 1

4

ではh(X &&)、 の型tは への右辺値参照Xですが、名前付き変数は常に左辺値として扱われます。したがって、 であっても、パラメーターに直接バインドすることはできませんが、tパラメーターのみにバインドできます。これは安全のためです。名前付き変数は何度も使用できる (そして頻繁に使用される) からです。変数がもともと右辺値にバインドされていたとしても、変数の最初の使用でそれを盗むことは望ましくありません。その後の使用では、盗まれた値が表示され、コード内のロジックが壊れやすくなります。X &&tX &&X &

変数が右辺値であることがわかっている場合 (または、それが左辺値であるか右辺値であるかに関係なく、それで作業が完了していることがわかっている場合は、それ以上)、それを r- 値として渡す方法値は使用によるものmove()です。の目的はforward<T>()、元の値を盗む必要があるかどうかがわからない場合の汎用コード用です。テンプレートで使用するmove()と、誤って左辺値を盗む可能性があります。したがってforward<T>()、代わりに使用します。これは、左辺値型の場合は無害なパススルーに解決され、非参照または右辺値参照の場合Tと本質的に同等になります。move()T

editでは、2 番目のオーバーロードh(つまりh(X &t)) がforward<>間違って使用されていることに注意してください。その場合の の型はtであるX &ため、 を使用する必要がありますforward<X&>(t)tこれを行うと、それが左辺値として渡されることがわかります。ただし、 の 2 つのオーバーロードではh、最初に右辺値参照があり、2 番目に左辺値参照があることがわかります。(テンプレートの推定は含まれないので、型はわかります。) したがって、move()最初のオーバーロードで直接使用し、2 番目のオーバーロードでは何も使用しない方がよいでしょう。の目的はforward<T>()、テンプレート推定から情報を収集して、それが左辺値または右辺値にバインドされている (および推定されている) かどうかを判断することです。

于 2013-07-09T05:50:10.777 に答える