値のカテゴリと、その参照Value categories, and references to them

このトピックでは、C++ に存在する値のさまざまなカテゴリ (および値の参照) について説明します。This topic describes the various categories of values (and references to values) that exist in C++. lvaluevalue については聞いたことがあると思いますが、このトピックで説明するような観点でそれらを考えたことはないかもしれません。You will doubtless have heard of lvalues and rvalues, but you may not think of them in the terms that this topic presents. また、値には他の種類もあります。And there are other kinds of values, too.

C++ のすべての式では、このトピックで説明するカテゴリのいずれかに属している値が生成されます。Every expression in C++ yields a value that belongs to one of the categories discussed in this topic. C++ 言語、その機能、およびルールの中には、値のカテゴリおよびその参照について適切に理解していることが必要な部分があります。There are aspects of the C++ language, its facilies, and rules, that demand a proper understanding of these value categories, and references to them. たとえば、値のアドレスの取得、値のコピー、値の移動、別の関数への値の転送などです。For example, taking the address of a value, copying a value, moving a value, and forwarding a value on to another function. このトピックでは、これらの側面のすべてについて詳しく説明はしませんが、深く理解するための基本情報を提供します。This topic doesn't go into all of those aspects in depth, but it provides foundational information for a solid understanding of them.

このトピックの情報は、Stroustrup の ID と移動可能性の 2 つの独立したプロパティによる値のカテゴリの分析の観点が基になっています [Stroustrup、2013 年]。The info in this topic is framed in terms of Stroustrup's analysis of value categories by the two independent properties of identity and movability [Stroustrup, 2013].

lvalue は ID を持っているAn lvalue has identity

値が ID を持っているとはどういうことでしょうか。What does it mean for a value to have identity? 値のメモリ アドレスがあり (または取得することができ)、それを安全に使用する場合、その値には ID があります。If you have (or you can take) the memory address of a value and use it safely, then the value has identity. 値の内容の比較以上のことができます。ID によって比較または区別できます。That way, you can do more than compare the contents of values: you can compare or distinguish them by identity.

lvalue には ID があります。An lvalue has identity. "lvalue" の "l" は "left" の頭文字ですが (代入の左辺)、これは今では歴史的な意味しかありません。It's now a matter of only historical interest that the "l" in "lvalue" is an abbreviation of "left" (as in, the left-hand-side of an assignment). C++ では、lvalue は代入の左辺 "または" 右辺のどちらでも使用できます。In C++, an lvalue can appear on the left or on the right of an assignment. "lvalues" の "l" は、実際にはそれが何であるかを理解または定義する役には立ちません。The "l" in "lvalues", then, doesn't actually help you to comprehend nor define what they are. lvalue と呼ばれるものは ID を持つ値である、と理解しておくことだけが必要です。You need only to understand that what we call an lvalue is a value that has identity.

lvalue である式の例としては、名前付きの変数や定数、または参照を返す関数などがあります。Examples of expressions that are lvalues include: a named variable or constant; or a function that returns a reference. lvalue では "ない" 式の例は、一時的な式や、値を返す関数などです。Examples of expressions that are not lvalues include: a temporary; or a function that returns by value.

int& get_by_ref() { ... }
int get_by_val() { ... }

int main()
{
    std::vector<byte> vec{ 99, 98, 97 };
    std::vector<byte>* addr1{ &vec }; // ok: vec is an lvalue.
    int* addr2{ &get_by_ref() }; // ok: get_by_ref() is an lvalue.

    int* addr3{ &(get_by_ref() + 1) }; // Error: get_by_ref() + 1 is not an lvalue.
    int* addr4{ &get_by_val() }; // Error: get_by_val() is not an lvalue.
}

現在は、lvalue は ID を持っているというのが正しい表現ですが、xvalue もやはり ID を持っています。Now, while it's a true statement that lvalues have identity, so do xvalues. xvalue については後でさらに詳しく説明します。We'll go more into what an xvalue is later in this topic. ここでは、glvalue ("generalized lvalue": 一般化された lvalue) と呼ばれる値のカテゴリがあることだけを憶えておいてください。For now, just be aware that there is a value category called glvalue, for "generalized lvalue". glvalue のスーパーセットには、lvalue ("従来の lvalue" とも呼ばれます) と xvalue の両方が含まれます。The superset of glvalues contains both lvalues (also known as classical lvalues) and xvalues. そのため、たしかに "lvalue は ID を持っています" が、ID を持つものの完全なセットは、次の図に示すように glvalue のセットです。So, while "an lvalue has identity" is true, the complete set of things that have identity is the set of glvalues, as shown in this illustration.

lvalue は ID を持っている

rvalue は移動可能であり、lvalue は移動可能ではないAn rvalue is movable; an lvalue is not

ただし、glvalue ではない値があります。But there are values that are not glvalues. したがって、メモリ アドレスを取得することが "できない" (または、それが有効であることに依存できない) 値があります。Consequently, there are values that you can't obtain a memory address for (or you can't rely on it to be valid). 上のコード例では、そのような値がいくつか示されています。We saw some such values in the code example above. これは欠点のように聞こえます。This sounds like a disadvantage. しかし、実際には、そのような値の利点として、値をコピーする (一般に高コスト) のではなく、値を "移動する" (一般的に低コスト) ことができます。But in fact the advantage of a value like that is that you can move from it (which is generally cheap), rather than copy from it (which is generally expensive). 値を移行するということは、それまであった場所に存在しなくなることを意味します。Moving from a value means that it's no longer in the place it used to be. そのため、前に使われていた場所にアクセスすることは、避ける必要があります。So, trying to access it in the place it used to be is something to be avoided. いつ、"どのようにして" 値を移動するのかについての説明は、このトピックの範囲外です。A discussion of when and how to move a value is out of scope for this topic. このトピックでは、移動可能な値が rvalue (または "従来の rvalue") と呼ばれることだけを知っておく必要があります。For this topic, we just need to know that a value that is movable is known as an rvalue (or classical rvalue).

"rvalue" の "r" は、"right" の頭文字です (代入の右辺)。The "r" in "rvalue" is an abbreviation of "right" (as in, the right-hand-side of an assignment). ただし、代入の外側でも、rvalue を使ったり、rvalue を参照したりできます。But you can use rvalues, and references to rvalues, outside of assignments. ですから、"rvalue" の "r" は重要な点ではありません。The "r" in "rvalues", then, is not the thing to focus on. rvalue と呼ばれるものは移動可能な値である、とだけ理解しておけば十分です。You need only to understand that what we call an rvalue is a value that is movable.

逆に、次の図のように、lvalue は移動可能ではありません。An lvalue, conversely, isn't movable, as shown in this illustration. 移動された lvalue は lvalue の定義に従わなくなり、lvalue に引き続きアクセスできることがごく当然に期待されるコードに対して予期しない問題になります。An lvalue that moved would defy the definition of lvalue, and it would be an unexpected problem for code that very reasonably expected to be able to continue to access the lvalue.

rvalue は移動可能であり、lvalue は移動可能ではない

lvalue を移動することはできません。You can't move an lvalue. ただし、自分が何を行っているのかを理解した上でなら (移動後にアクセスしないように注意することなど) 移動できる glvalue の種類が "あります"。それが xvalue です。But there is a kind of glvalue (the set of things with identity) that you can move—if you know what you're doing (including being careful not to access it after the move)—and that's the xvalue. このアイデアについては、後で値のカテゴリについて詳しく見るときに改めて説明します。We'll revisit this idea one more time below, when we look at the complete picture of value categories.

rvalue 参照と参照バインド ルールRvalue references, and reference-binding rules

このセクションでは、rvalue を参照するときの構文について説明します。This section introduces the syntax for a reference to an rvalue. 移動および転送の処理の多くについては別のトピックで説明されていますが、それらは rvalue 参照によって解決される問題です。We'll have to wait for another topic to go into a substantial treatment of moving and forwarding, but those are problems that are solved by rvalue references. ただし、rvalue 参照について説明する前にまず、T& についてはっきりさせておく必要があります。つまり、これまで単に "参照" と呼んでいた事柄です。Before we look at rvalue references, though, we first need to be clearer about T&—the thing we've formerly been calling just "a reference". それは本当は "lvalue (非定数) の参照" であり、参照のユーザーが書き込むことのできる値を参照しています。It's really "an lvalue (non-const) reference", which refers to an value to which the user of the reference can write.

template<typename T> T& get_by_lvalue_ref() { ... } // Get by lvalue (non-const) reference.
template<typename T> void set_by_lvalue_ref(T&) { ... } // Set by lvalue (non-const) reference.

lvalue の参照は lvalue にはバインドできますが、rvalue にはバインドできません。An lvalue reference can bind to an lvalue, but not to an rvalue.

それから、lvalue の const 参照が (T const&) があり、これは参照のユーザーが書き込むことの "できない" オブジェクトを参照します (たとえば、定数)。Then there are lvalue const references (T const&), which refer to objects to which the user of the reference can't write (for example, a constant).

template<typename T> T const& get_by_lvalue_cref() { ... } // Get by lvalue const reference.
template<typename T> void set_by_lvalue_cref(T const&) { ... } // Set by lvalue const reference.

lvalue の const 参照は、lvalue または rvalue にバインドできます。An lvalue const reference can bind to an lvalue or to an rvalue.

T 型の rvalue への参照の構文は、T&& と記述されます。The syntax for a reference to an rvalue of type T is written as T&&. rvalue 参照では、移動可能な値、つまり使用後に内容を保持する必要がない値が参照されます (たとえば、一時的な値)。An rvalue reference refers to a movable value—an value whose contents we don't need to preserve after we've used it (for example, a temporary). 核心は rvalue 参照にバインドされている値を移動する (それによって変更する) ことなので、const および volatile 修飾子 (cv 修飾子とも呼ばれます) は、rvalue の参照には適用されません。Since the whole point is to move from (thereby modifying) the value bound to an rvalue reference, const and volatile qualifiers (also known as cv-qualifiers) don't apply to rvalue references.

template<typename T> T&& get_by_rvalue_ref() { ... } // Get by rvalue reference.
struct A { A(A&& other) { ... } }; // A move constructor takes an rvalue reference.

rvalue 参照は、rvalue にバインドされます。An rvalue reference binds to an rvalue. 実際、オーバーロードの解決では、rvalue は lvalue の const 参照より rvalue 参照にバインドする方を "優先されます"。In fact, in terms of overload resolution, an rvalue prefers to be bound to an rvalue reference than to an lvalue const reference. ただし、前に説明したように、rvalue 参照では内容を保持する必要がないものと見なされる値 (たとえば、移動コンストラクターのパラメーター) を参照するので、rvalue 参照を lvalue にバインドすることはできません。But an rvalue reference can't bind to an lvalue because, as we've said, an rvalue reference refers to a value whose contents it's assumed we don't need to preserve (say, the parameter for a move constructor).

また、コピー構造 (または、rvalue が xvalue の場合は移動構造) を介して、値渡しの引数が期待される rvalue を渡すこともできます。You can also pass an rvalue where a by-value argument is expected, via copy construction (or via move construction, if the rvalue is an xvalue).

glvalue には ID があるが、prvalue にはないA glvalue has identity; a prvalue does not

ここまでで、ID を持っているものがわかりました。At this stage, we know what has identity. また、移動できるものとできないものがわかりました。And we know what's movable and what isn't. しかし、ID を持って "いない" 値のセットには、まだ名前を付けていません。But we haven't yet named the set of values that don't have identity. そのようなセットは、prvalue (pure rvalue: 純粋な rvalue) と呼ばれます。That set is known as the prvalue, or pure rvalue.

int& get_by_ref() { ... }
int get_by_val() { ... }

int main()
{
    int* addr3{ &(get_by_ref() + 1) }; // Error: get_by_ref() + 1 is a prvalue.
    int* addr4{ &get_by_val() }; // Error: get_by_val() is a prvalue.
}

lvalue には ID があるが、prvalue にはない

値のカテゴリの全体像The complete picture of value categories

残っているのは、これまでに示した情報と図を結合して、1 つの大きな図にすることだけです。It only remains to combine the info and illustrations above into a single, big picture.

値のカテゴリの全体像

glvalue (i)glvalue (i)

glvalue (一般化された lvalue) は、ID を持っています。A glvalue (generalized lvalue) has identity.

lvalue (i&!m)lvalue (i&!m)

lvalue (glvalue の一種) は ID を持っていますが、移動することはできません。An lvalue (a kind of glvalue) has identity, but isn't movable. これらは、通常は読み取り/書き込みの値であり、参照、const 参照、またはコピーが低コストの場合は値によって、渡されます。These are typically read-write values that you pass around by reference or by const reference, or by value if copying is cheap. lvalue を rvalue 参照にバインドすることはできません。An lvalue can't be bound to an rvalue reference.

xvalue (i&m)xvalue (i&m)

xvalue (glvalue の一種だが、rvalue の一種でもある) は ID を持っており、移動することもできます。An xvalue (a kind of glvalue, but also a kind of rvalue) has identity, and is also movable. これは、コピーが高コストであるために移動することにしたかつての lvalue である場合があり、後でアクセスしないように注意する必要があります。This might be an erstwhile lvalue that you've decided to move because copying is expensive, and you'll be careful not to access it afterward. lvalue を xvalue に変換する方法を次に示します。Here's how you can turn an lvalue into an xvalue.

struct A { ... };
A a; // a is an lvalue...
static_cast<A&&>(a); // ...but this expression is an xvalue.

上記のコード例では、まだ何も移動していません。In the code example above, we haven't moved anything yet. lvalue を名前のない rvalue 参照にキャストして xvalue を作成しただけです。We've just created an xvalue by casting an lvalue to an unnamed rvalue reference. まだ lvalue の名前で識別できますが、xvalue として移動 "できる" ようになっています。It can still be identified by its lvalue name; but, as an xvalue, it is now capable of being moved. このようなことを行う理由、および実際の移動については、別のトピックをご覧ください。The reasons for doing so, and what moving actually looks like, will have to wait for another topic. ただし、それが役に立つなら、"xvalue" の "x" は "expert-only" (エキスパート専用) と考えることができます。But you can think of the "x" in "xvalue" as meaning "expert-only" if that helps. lvalue を xvalue (rvalue の一種) にキャストすることによって、値は rvalue 参照にバインドできるようになります。By casting an lvalue into an xvalue (a kind of rvalue), the value then becomes capable of being bound to an rvalue reference.

次に示すのは xvalue の他の 2 つの例で、名前のない rvalue 参照を返す関数の呼び出しと、xvalue のメンバーへのアクセスです。Here are two other examples of xvalues—calling a function that returns an unnamed rvalue reference, and accessing a member of an xvalue.

struct A { int m; };
A&& f();
f(); // This expression is an xvalue...
f().m; // ...and so is this.

prvalue (!i&m)prvalue (!i&m)

prvalue (純粋な rvalue、rvalue の一種) は ID を持ちませんが、移動することはできます。A prvalue (pure rvalue; a kind of rvalue) doesn't have identity, but is movable. 通常、これらは一時的な値、値を返す関数の呼び出しの結果、または glvalue ではない他の式の評価の結果ですThese are typically temporaries, the result of calling a function that returns by value, or the result of evaluating any other expression that's not a glvalue,

rvalue (m)rvalue (m)

rvalue は移動可能です。An rvalue is movable. rvalue "参照" では、常に rvalue が参照されます (内容を保持する必要がないものと思われる値)。An rvalue reference always refers to an rvalue (a value whose contents it's assumed we don't need to preserve).

ところで、rvalue 参照自体は rvalue なのでしょうか。But, is an rvalue reference itself an rvalue? "名前のない" rvalue 参照 (上の xvalue コードの例にで示されているものなど) は xvalue なので、rvalue です。An unnamed rvalue reference (like the ones shown in the xvalue code examples above) is an xvalue so, yes, it's an rvalue. 移動コンストラクターなど、rvalue 参照関数パラメーターへのバインドが優先されます。It prefers to be bound to an rvalue reference function parameter, such as that of a move constructor. 逆に (そして、おそらくは直感に反しますが)、rvalue 参照が名前を持っている場合、その名前で構成される式は lvalue です。Conversely (and perhaps counter-intuitively), if an rvalue reference has a name, then the expression consisting of that name is an lvalue. したがって、rvalue 参照パラメーターにバインドすることは "できません"。So it can't be bound to an rvalue reference parameter. ただし、名前のない rvalue 参照 (xvalue) に再びキャストするだけで、簡単にそのようにすることができます。But it's easy to make it do so—just cast it to an unnamed rvalue reference (an xvalue) again.

void foo(A&) { ... }
void foo(A&&) { ... }
void bar(A&& a) // a is a named rvalue reference; it's an lvalue.
{
    foo(a); // Calls foo(A&).
    foo(static_cast<A&&>(a)); // Calls foo(A&&).
}
A&& get_by_rvalue_ref() { ... } // This unnamed rvalue reference is an xvalue.

!i&!m!i&!m

ID を持たず、移動できない値の種類は、まだ説明していない組み合わせです。The kind of value that doesn't have identity and isn't movable is the one combination that we haven't yet discussed. しかし、そのカテゴリは C++ 言語で役に立つアイデアではないので、無視してかまいません。But we can disregard it, because that category isn't a useful idea in the C++ language.

参照縮小ルールReference-collapsing rules

式に含まれる複数の似た参照 (lvalue 参照に対する lvalue 参照、または rvalue 参照に対する rvalue 参照) は、相互に相殺されます。Multiple like references in an expression (an lvalue reference to an lvalue reference, or an rvalue reference to an rvalue reference) cancel one another out.

  • A& &A& に縮小されます。A& & collapses into A&.
  • A&& &&A&& に縮小されます。A&& && collapses into A&&.

式に含まれる複数の似ていない参照は、lvalue 参照に縮小されます。Multiple unlike references in an expression collapse to an lvalue reference.

  • A& &&A& に縮小されます。A& && collapses into A&.
  • A&& &A& に縮小されます。A&& & collapses into A&.

転送参照Forwarding references

この最後のセクションでは、既に説明した rvalue 参照と、別の概念である "転送参照" を比べます。This final section contrasts rvalue references, which we've already discussed, with the different concept of a forwarding reference.

void foo(A&& a) { ... }
  • 前に見たように、A&& は rvalue 参照です。A&& is an rvalue reference, as we've seen. const と volatile は、rvalue 参照には適用されません。Const and volatile don't apply to rvalue references.
  • foo は、A 型の rvalue のみを受け付けます。foo accepts only rvalues of type A.
  • rvalue 参照 (A&& など) が存在する理由は、一時的な値 (またはその他の rvalue) が渡される場合に最適化されたオーバーロードを作成できるようにするためです。The reason rvalue references (such as A&&) exist is so that you can author an overload that's optimized for the case of a temporary (or other rvalue) being passed.
template <typename _Ty> void bar(_Ty&& ty) { ... }
  • _Ty&& は "転送参照" です。_Ty&& is a forwarding reference. bar に渡すものに応じて、 _Ty 型を、volatile/非 volatile とは無関係に、const/非 const にできます。Depending what you pass to bar, type _Ty could be const/non-const independently of volatile/non-volatile.
  • bar では、 _Ty 型の任意の lvalue または rvalue が受け付けられます。bar accepts any lvalue or rvalue of type _Ty.
  • lvalue を渡すと、転送参照は _Ty& && になり、lvalue 参照 _Ty& に縮小されます。Passing an lvalue causes the forwarding reference to become _Ty& &&, which collapses to the lvalue reference _Ty&.
  • rvalue を渡すと、転送参照は _Ty&& && になり、rvalue 参照 _Ty&& に縮小されます。Passing an rvalue causes the forwarding reference to become _Ty&& &&, which collapses to the rvalue reference _Ty&&.
  • 転送参照 (_Ty&& など) が存在する理由は、最適化のためでは "なく"、渡されたものを取得して、透過的かつ効率的に転送するためです。The reason forwarding references (such as _Ty&&) exist is not for optimization, but to take what you pass to them and to forward it on transparently and efficiently. おそらく、転送参照が出てくるのは、ライブラリ コードを書く (または、詳しく学習する) 場合だけです。たとえば、コンストラクターの引数に転送するファクトリ関数などです。You're likely to encounter a forwarding reference only if you write (or closely study) library code—for example, a factory function that forwards on constructor arguments.

SourcesSources

  • [Stroustrup、2013 年] B. Stroustrup: C++ プログラミング言語、第 4 版。[Stroustrup, 2013] B. Stroustrup: The C++ Programming Language, Fourth Edition. Addison-Wesley.Addison-Wesley. 2013 年。2013.