参照初期化
参照をオブジェクトに束縛します。
目次 |
[編集] 構文
[編集] リスト初期化以外
T & ref = target ;T |
(1) | ||||||||
T && ref = target ;T |
(2) | (C++11以降) | |||||||
func-refpar ( target ) |
(3) | ||||||||
return target ; |
(4) | (func-refretの定義内) | |||||||
Class::Class(...) : ref-member ( target ) { ... } |
(5) | (Classの定義内) | |||||||
[編集] 通常のリスト初期化 (C++11以降)
T & ref = { arg1, arg2, ... };T |
(1) | ||||||||
T && ref = { arg1, arg2, ... };T |
(2) | ||||||||
func-refpar ({ arg1, arg2, ... }); |
(3) | ||||||||
[編集] 指定初期化リスト (C++20以降)
T & ref = {.des1 = arg1 , .des2 { arg2 } ... };T |
(1) | ||||||||
T && ref = {.des1 = arg1 , .des2 { arg2 } ... };T |
(2) | ||||||||
func-refpar ({.des1 = arg1 , .des2 { arg2 } ... }); |
(3) | ||||||||
Tへの参照は、T型のオブジェクト、T型の関数、またはTに暗黙的に変換可能なオブジェクトで初期化できます。初期化された参照は、別のオブジェクトを参照するように再設定(変更)することはできません。
参照は以下の状況で初期化されます。
[編集] 説明
| T | - | 参照される型 |
| ref | - | 初期化される参照変数 |
| target | - | 使用される初期化子式 |
| func-refpar | - | 参照型のパラメータを持つ関数(T &またはT &&(C++11以降)) |
| func-refret | - | 戻り値の型が参照型である関数(T &またはT &&(C++11以降)) |
| Class | - | クラス名 |
| ref-member | - | Classの非静的メンバ変数で参照型(T &またはT &&(C++11以降)) |
| des1, des2, ... | - | 指定子 |
| arg1, arg2, ... | - | 初期化子リスト内の初期化子 |
[編集] 定義
2つの型T1とT2について
T1とT2のcv修飾なしのバージョンをそれぞれU1とU2とする。U1がU2と類似しているか、またはU1がU2の基底クラスである場合、T1はT2と*参照関連*である。- 「
T2へのポインタ」というprvalueが標準変換シーケンスを介して「T1へのポインタ」という型に変換できる場合、T1はT2と*参照互換*である。
[編集] 初期化規則
|
参照初期化で通常のまたは指定された(C++20以降)リスト初期化が使用される場合、リスト初期化の規則に従います。 |
(C++11以降) |
リスト初期化以外の場合、targetの型をUとして、参照はtargetに*直接束縛*されるか、またはtargetから変換されたT型の値に束縛されます。直接束縛が最初に考慮され、次に間接束縛が考慮されます。どちらの束縛も利用できない場合、プログラムは不適格です。
2つの型の参照互換関係が参照束縛の有効性を確立するために使用され、標準変換シーケンスが不適格となるすべてのケースにおいて、そのような束縛を必要とするプログラムは不適格です。
[編集] 直接束縛
以下のすべての条件が満たされる場合
- 初期化される参照は左辺値参照である。
- targetはビットフィールドではない左辺値である。
-
TはUと参照互換である。
この場合、参照はtarget、またはその適切な基底クラスサブオブジェクトに束縛されます。
double d = 2.0; double& rd = d; // rd refers to d const double& rcd = d; // rcd refers to d struct A {}; struct B : A {} b; A& ra = b; // ra refers to A subobject in b const A& rca = b; // rca refers to A subobject in b
そうでなく、以下のすべての条件が満たされる場合
- 初期化される参照は左辺値参照である。
-
Uはクラス型である。 -
TはUと参照関連ではない。 - targetを
V型の左辺値に変換でき、TがVと参照互換である。
この場合、参照は変換の左辺値結果、またはその適切な基底クラスサブオブジェクトに束縛されます。
struct A {}; struct B : A { operator int&(); }; int& ir = B(); // ir refers to the result of B::operator int&
そうでなく、初期化される参照が左辺値参照であり、Tがconst修飾されていないか、またはvolatile修飾されている場合、プログラムは不適格です。
double& rd2 = 2.0; // error: not an lvalue and reference is not const int i = 2; double& rd3 = i; // error: type mismatch and reference is not const
そうでなく、以下のすべての条件が満たされる場合
- targetは以下のいずれかのカテゴリの値である。
|
(C++11まで) |
|
(C++11以降) (C++17まで) |
|
(C++17以降) |
-
TはUと参照互換である。
この場合、参照はtarget、またはその適切な基底クラスサブオブジェクトに束縛されます。
struct A {}; struct B : A {}; extern B f(); const A& rca2 = f(); // bound to the A subobject of the B rvalue. A&& rra = f(); // same as above int i2 = 42; int&& rri = static_cast<int&&>(i2); // bound directly to i2
|
targetがprvalueの場合、一時オブジェクト化が適用され、prvalueの型は調整型
この場合、参照は結果オブジェクト、またはその適切な基底クラスサブオブジェクトに束縛されます。 |
(C++17以降) |
そうでなく、以下のすべての条件が満たされる場合
-
Uはクラス型である。 -
TはUと参照関連ではない。 - targetを
V型の値vに変換でき、TがVと参照互換である場合、ここでvは以下のいずれかのカテゴリの値である。
|
(C++11まで) |
|
(C++11以降) (C++17まで) |
|
(C++17以降) |
この場合、参照は変換の結果、またはその適切な基底クラスサブオブジェクトに束縛されます。
struct A {}; struct B : A {}; struct X { operator B(); } x; const A& r = x; // bound to the A subobject of the result of the conversion B&& rrb = x; // bound directly to the result of the conversion
|
変換の結果がprvalueの場合、一時オブジェクト化が適用され、prvalueの型は調整型
この場合、参照は結果オブジェクト、またはその適切な基底クラスサブオブジェクトに束縛されます。 |
(C++17以降) |
[編集] 間接束縛
直接束縛が利用できない場合、間接束縛が考慮されます。この場合、TはUと参照関連ではありません。
TまたはUがクラス型の場合、ユーザー定義変換は、型Tのオブジェクトに対するコピー初期化の規則を使用して考慮されます。参照ではないコピー初期化が不適格となる場合、プログラムは不適格です。非参照コピー初期化で説明されている変換関数の呼び出し結果が、参照の直接初期化に使用されます。この直接初期化では、ユーザー定義変換は考慮されません。
|
それ以外の場合、型 |
(C++17まで) |
|
それ以外の場合、targetは「cv修飾なし |
(C++17以降) |
const std::string& rs = "abc"; // rs refers to temporary copy-initialized from char array const double& rcd2 = 2; // rcd2 refers to temporary with value 2.0 int i3 = 2; double&& rrd3 = i3; // rrd3 refers to temporary with value 2.0
[編集] 一時オブジェクトの生存期間
参照が一時オブジェクトまたはそのサブオブジェクトに束縛される場合、一時オブジェクトの生存期間は参照の生存期間に一致するように延長されます(一時オブジェクトの生存期間の例外を参照)。ここで、一時オブジェクトまたはそのサブオブジェクトは、以下のいずれかの式によって示されます。
|
(C++17まで) |
| (C++17以降) |
- これらの式のいずれかである括弧で囲まれた式 (e)。
- これらの式のいずれかである配列
aおよび整数nの組み込み添字演算子の形式 a[n] または n[a]。 - これらの式のいずれかである式
eと、オブジェクト型の非静的メンバmを指定するクラスメンバアクセス演算子の形式 e.m。 - これらの式のいずれかである式
eと、データメンバへのポインタであるmpを指定するポインタtoメンバ演算の形式 e.*mp。 - ユーザー定義変換のない
const_cast、static_cast、dynamic_cast、またはreinterpret_cast変換。これは、これらの式のいずれかをオペランドによって指定されるオブジェクト、またはその完全オブジェクトもしくはサブオブジェクトに変換します(明示的キャスト式はこれらのキャストのシーケンスとして解釈されます)。 - 左辺値である形式 cond ? e1 : e2 の条件演算子。ここで、
e1またはe2はこれらの式のいずれかです。 - 左辺値である形式 x, e の組み込みカンマ演算子。ここで、
eはこれらの式のいずれかです。
この生存期間の規則には以下の例外があります。
|
(C++26まで) |
- 関数呼び出しの参照パラメータに束縛された一時オブジェクトは、その関数呼び出しを含むフル式が終了するまで存在します。関数が参照を返す場合、その参照がフル式より長く存続すると、ダングリング参照になります。
|
(C++11以降) |
struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference |
(C++20以降) |
一般に、一時オブジェクトの生存期間は「渡し直す」ことによってさらに延長することはできません。一時オブジェクトが束縛されていた参照変数またはデータメンバから初期化された2番目の参照は、その生存期間に影響しません。
[編集] 注釈
初期化子なしの参照は、関数パラメータ宣言、関数戻り値型宣言、クラスメンバの宣言、およびextern指定子にのみ現れます。
CWG issue 1696の解決まで、コンストラクタの初期化子リストで参照メンバに一時オブジェクトを束縛することが許可されていました。その場合、オブジェクトの生存期間ではなく、コンストラクタが終了するまで存続します。このような初期化はCWG 1696以降不適格ですが、多くのコンパイラは(clangを除く)まだサポートしています。
[編集] 例
#include <sstream> #include <utility> struct S { int mi; const std::pair<int, int>& mp; // reference member }; void foo(int) {} struct A {}; struct B : A { int n; operator int&() { return n; } }; B bar() { return B(); } //int& bad_r; // error: no initializer extern int& ext_r; // OK int main() { // Lvalues int n = 1; int& r1 = n; // lvalue reference to the object n const int& cr(n); // reference can be more cv-qualified volatile int& cv{n}; // any initializer syntax can be used int& r2 = r1; // another lvalue reference to the object n // int& bad = cr; // error: less cv-qualified int& r3 = const_cast<int&>(cr); // const_cast is needed void (&rf)(int) = foo; // lvalue reference to function int ar[3]; int (&ra)[3] = ar; // lvalue reference to array B b; A& base_ref = b; // reference to base subobject int& converted_ref = b; // reference to the result of a conversion // Rvalues // int& bad = 1; // error: cannot bind lvalue ref to rvalue const int& cref = 1; // bound to rvalue int&& rref = 1; // bound to rvalue const A& cref2 = bar(); // reference to A subobject of B temporary A&& rref2 = bar(); // same int&& xref = static_cast<int&&>(n); // bind directly to n // int&& copy_ref = n; // error: can't bind to an lvalue double&& copy_ref = n; // bind to an rvalue temporary with value 1.0 // Restrictions on temporary lifetimes // std::ostream& buf_ref = std::ostringstream() << 'a'; // the ostringstream temporary was bound to the left operand // of operator<< but its lifetime ended at the semicolon so // the buf_ref is a dangling reference S a {1, {2, 3}}; // temporary pair {2, 3} bound to the reference member // a.mp and its lifetime is extended to match // the lifetime of object a S* p = new S{1, {2, 3}}; // temporary pair {2, 3} bound to the reference // member p->mp, but its lifetime ended at the semicolon // p->mp is a dangling reference delete p; // Imitate [[maybe_unused]] applied to the following variables: [](...){} ( cv, r2, r3, rf, ra, base_ref, converted_ref, a, cref, rref, cref2, rref2, copy_ref, xref ); }
[編集] 不具合報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 391 | C++98 | const修飾された型への参照をクラス型で初期化する 右辺値が一時オブジェクトを作成する可能性があり、そのクラスのコンストラクタが 右辺値をその一時オブジェクトにコピーするために必要でした |
一時オブジェクトは作成されず、コンストラクタは 必要ありません 必要ありません |
| CWG 450 | C++98 | const修飾された配列への参照を、参照互換な配列右辺値で 初期化できませんでした |
許可 |
| CWG 589 | C++98 | 参照が配列またはクラスの右辺値に直接束縛できない | 許可 |
| CWG 656 | C++98 | const修飾された型への参照が、参照互換ではなく 参照互換な型への変換関数を持つ型で初期化された場合、 変換関数の戻り値(またはその基底クラスサブオブジェクト)からコピーされた一時オブジェクトに束縛されました 直接束縛されました |
戻り値(またはその基底クラス サブオブジェクト)に 直接 |
| CWG 1287 | C++11 | クラス型のtargetから別の 参照互換な型への変換は、暗黙的である必要がありました |
明示的な変換を許可 変換を適用できる |
| CWG 1295 | C++11 | 参照がビットフィールドxvalueに束縛できる | 禁止された |
| CWG 1299 | C++98 | 一時オブジェクトの定義が不明確でした | 明確化された |
| CWG 1571 | C++98 | 間接束縛におけるユーザー定義変換は、targetの型を考慮していませんでした 考慮していませんでした |
考慮された |
| CWG 1604 | C++98 | 間接束縛ではユーザー定義変換が考慮されていませんでした | 考慮された |
| CWG 2352 | C++98 | 参照互換性ではcv修飾変換が考慮されていませんでした | 考慮された |
| CWG 2481 | C++17 | 間接束縛における一時オブジェクト化の結果型にcv修飾子が追加されていませんでした 追加されていませんでした |
追加された |
| CWG 2657 | C++17 | 間接束縛における一時オブジェクト化の結果型にcv修飾子が追加されていませんでした 直接束縛における一時オブジェクト化 |
追加された |
| CWG 2801 | C++98 | 間接束縛で参照関連型が許可されていました | 禁止された |