サーチ…
値カテゴリの意味
C ++の式には、その式の結果に基づいて特定の値カテゴリが割り当てられます。式の値カテゴリは、C ++関数のオーバーロード解決に影響します。
値のカテゴリは、式に関する2つの重要な、別々のプロパティを決定します。 1つの特性は、その表現が同一性を有するかどうかである。変数名を持つオブジェクトを参照する場合、式は同一性を持ちます。変数名は式に関係しないかもしれませんが、オブジェクトにはまだ変数名が含まれています。
もう1つのプロパティは、式の値から暗黙的に移動することが合法かどうかです。より具体的には、式を関数パラメータとして使用する場合、r値のパラメータ型にバインドするかどうかを指定します。
C ++は、lvalue(識別可能ではあるが移動可能ではない式)、xvalue(識別可能な式は移動可能)、およびprvalue(識別可能な式がない式)の3つの値のカテゴリを定義しています。 C ++にはIDがなく、移動できない式はありません。
C ++では、glvalue(ID付きの式)とrvalue(移動可能な式)の2つの値カテゴリが定義されています。これらは、先のカテゴリの有用なグループ分けとして機能する。
このグラフはイラストとして役立ちます:
値段付け
prvalue(純粋な値)式は、同一性がない式で、その評価は通常オブジェクトの初期化に使用され、暗黙的に移動できます。これには以下のものが含まれますが、これらに限定されません。
- 一時オブジェクトを表す式
std::string("123")
。 - 参照を返さない関数呼び出し式
- リテラル(文字列リテラルを除く - これは左辺値です)、
1
、true
、0.5f
、または'a'
- ラムダ式
組み込みアドレスの演算子( &
)は、これらの式には適用できません。
xvalue
xvalue(eXpiring値)式は、IDを持ち、暗黙的に移動できるオブジェクトを表す式です。 xvalue式の一般的な考え方は、それらが表現するオブジェクトがすぐに破棄されることになり(したがって、「エキサイティング」な部分)、暗黙のうちにそこから移動することは問題ないということです。
与えられた:
struct X { int n; };
extern X x;
4; // prvalue: does not have an identity
x; // lvalue
x.n; // lvalue
std::move(x); // xvalue
std::forward<X&>(x); // lvalue
X{4}; // prvalue: does not have an identity
X{4}.n; // xvalue: does have an identity and denotes resources
// that can be reused
左辺値
左辺式は、同一性を持つ式ですが、暗黙的に移動することはできません。これらの中には、変数名、関数名、組み込み逆参照演算子である式、左辺値参照を参照する式からなる式があります。
典型的な左辺値は単なる名前ですが、左辺値は他の味でも来ることができます:
struct X { ... };
X x; // x is an lvalue
X* px = &x; // px is an lvalue
*px = X{}; // *px is also an lvalue, X{} is a prvalue
X* foo_ptr(); // foo_ptr() is a prvalue
X& foo_ref(); // foo_ref() is an lvalue
さらに、ほとんどのリテラル(たとえば4
、 'x'
など)はprvalueですが、文字列リテラルは左辺値です。
glvalue
glvalue(「一般化された左辺値」)式は、移動可能かどうかにかかわらず、同一性を持つ式です。このカテゴリには、lvalues(同一性はあるが移動できない式)とxvalues(同一性を持ち、移動可能な式)が含まれますが、prvalues(同一性のない式)は除きます。
式に名前がある場合、それはglvalueです。
struct X { int n; };
X foo();
X x;
x; // has a name, so it's a glvalue
std::move(x); // has a name (we're moving from "x"), so it's a glvalue
// can be moved from, so it's an xvalue not an lvalue
foo(); // has no name, so is a prvalue, not a glvalue
X{}; // temporary has no name, so is a prvalue, not a glvalue
X{}.n; // HAS a name, so is a glvalue. can be moved from, so it's an xvalue
右値
rvalue式は、同一性を持っているかどうかにかかわらず、暗黙的に移動できる任意の式です。
より正確には、rvalue式は、 T &&
(ここでT
はexpr
の型です)型のパラメータをとる関数の引数として使用できます。このような関数の引数には、rvalue式のみを引数として与えることができます。非右辺の式が使用されている場合、過負荷解決はrvalue参照パラメータを使用しない関数を選択します。そして、存在しなければ、あなたはエラーを受け取ります。
rvalue式のカテゴリには、すべてのxvalue式とprvalue式、およびそれらの式のみが含まれます。
標準ライブラリ関数std::move
は、非右辺の式を右辺に明示的に変換するために存在します。より具体的には、式をxvalueに変えます。これは、前回のIDなしのprvalue式であっても、 std::move
パラメータとして渡すことで、ID(関数のパラメータ名)を取得してxvalueになるからです。
次の点を考慮してください。
std::string str("init"); //1
std::string test1(str); //2
std::string test2(std::move(str)); //3
str = std::string("new value"); //4
std::string &&str_ref = std::move(str); //5
std::string test3(str_ref); //6
std::string
は、 std::string&&
タイプの単一のパラメータを取るコンストラクタがあります。これは一般に「ムーブコンストラクタ」と呼ばれます。ただし、式str
値カテゴリはrvalue(特に左辺値)ではないため、そのコンストラクタをオーバーロードと呼び出すことはできません。代わりに、 const std::string&
overload(コピーコンストラクタ)を呼び出します。
3行目は物事を変えます。 std::move
の戻り値はT&&
で、 T
は渡されたパラメータの基本型です。したがって、 std::move(str)
はstd::string&&
返します。戻り値がrvalue参照である関数呼び出しはrvalue式(特にxvalue)であるため、 std::string
移動コンストラクタを呼び出すことができstd::string
。 3行目の後、 str
が移動しました(現在の内容は未定義です)。
4行目は一時的にstd::string
代入演算子に渡しstd::string
。これにはstd::string&&
をとるオーバーロードがあります。式std::string("new value")
はrvalue式(特にprvalue)であるため、そのオーバーロードを呼び出すことがあります。したがって、一時的なstr
に移動され、未定義の内容は特定の内容に置き換えられstr
。
5行目は、 str
を参照するstr_ref
という名前のstr_ref
参照を作成しstr
。これは、値のカテゴリが混乱するところです。
str_ref
はstd::string
右辺値参照ですが、式str_ref
値カテゴリは右辺値ではありません 。それは左辺値式です。はい、そうです。このため、 std::string
移動コンストラクタを式str_ref
で呼び出すことはできません。したがって、行6はstr
の値をstr
にコピーしtest3
。
それを移動するには、 std::move
再度使用する必要があります。