Reference initialization
オブジェクトへの参照をバインドします。
目次 |
構文
非リスト初期化
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++固有の用語はすべて原文のまま維持されています。
指定子リスト初期化 (C++20以降)
T
&
参照
= {.
識別子1
=
引数1
, .
識別子2
{
引数2
}
...
};
T
|
(1) | ||||||||
T
&&
参照
= {.
識別子1
=
引数1
, .
識別子2
{
引数2
}
...
};
T
|
(2) | ||||||||
関数参照引数
({.
識別子1
=
引数1
, .
識別子2
{
引数2
}
...
});
|
(3) | ||||||||
T
への参照は、
T
型のオブジェクト、
T
型の関数、または
T
へ暗黙的に変換可能なオブジェクトで初期化できます。一度初期化されると、参照を他のオブジェクトへ再設定(変更)することはできません。
参照は以下の状況で初期化されます:
説明
| T | - | 参照される型 |
| ref | - | 初期化される参照変数 |
| target | - | 初期化子式 |
| func-refpar | - |
参照型のパラメータを持つ関数 (
T
&
または
T
&&
(C++11以降)
)
|
| func-refret | - |
戻り値型が参照型である関数 (
T
&
または
T
&&
(C++11以降)
)
|
| Class | - | クラス名 |
| ref-member | - |
参照型の非静的データメンバ (
T
&
または
T
&&
(C++11以降)
) の
Class
|
| des1 , des2 , ... | - | 指示子 |
| arg1 , arg2 , ... | - | 初期化子リスト内の初期化子 |
定義
2つの型
T1
と
T2
について:
-
cv修飾されていないバージョンの
T1とT2がそれぞれU1とU2であるとき、U1が 類似 であるか、またはU1がU2の 基底クラス である場合、T1はT2に対して 参照関連 である。 -
「
T2へのポインタ」型のprvalueが標準変換シーケンスを通じて「T1へのポインタ」型に変換可能である場合、T1はT2と 参照互換 である。
初期化ルール
|
参照初期化が通常の または指定された (C++20以降) リスト初期化を使用する場合、 list-initialization の規則が適用されます。 |
(C++11以降) |
非リスト参照初期化において、
target
の型を
U
として与えた場合、参照は
target
に
直接バインド
するか、あるいは
target
から変換された型
T
の値にバインドします。直接バインドが最初に考慮され、次に間接バインドが考慮されます。どちらのバインドも利用できない場合、プログラムは不適格となります。
2つの型の参照互換性の関係が参照束縛の有効性を確立するために使用されるすべての場合において、標準変換シーケンスが不適格となる場合、そのような束縛を必要とするプログラムは不適格です。
ダイレクトバインディング
以下のすべての条件が満たされる場合:
- 初期化される参照は左辺値参照です。
- target は非- ビットフィールド の左辺値です。
-
TはUと参照互換性があります。
その後、参照は target に、またはその適切な基底クラスの部分オブジェクトにバインドされます:
double d = 2.0; double& rd = d; // rdはdを参照する const double& rcd = d; // rcdはdを参照する struct A {}; struct B : A {} b; A& ra = b; // raはb内のAサブオブジェクトを参照する const A& rca = b; // rcaはb内のAサブオブジェクトを参照する
それ以外の場合、以下のすべての条件が満たされる場合:
- 初期化される参照は左辺値参照です。
-
Uはクラス型です。 -
TはUと参照関連(reference-related)していません。 -
target
は型
Vの左辺値に変換可能であり、TはVと参照互換(reference-compatible)です。
その後、参照は変換の結果である左辺値、またはその適切な基底クラスの部分オブジェクトにバインドされます:
struct A {}; struct B : A { operator int&(); }; int& ir = B(); // irはB::operator int&の結果を参照する
それ以外の場合、初期化される参照が左辺値参照であり、かつ
T
がconst修飾されていない、またはvolatile修飾されている場合、プログラムは不適格となります:
double& rd2 = 2.0; // エラー: 左辺値ではなく、参照がconstではない int i = 2; double& rd3 = i; // エラー: 型が一致せず、参照が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(); // Bの右値のAサブオブジェクトにバインド A&& rra = f(); // 同上 int i2 = 42; int&& rri = static_cast<int&&>(i2); // 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; // 変換結果のAサブオブジェクトにバインド B&& rrb = x; // 変換結果に直接バインド
|
変換の結果がprvalueである場合、
一時オブジェクト化
が適用され、prvalueの型を調整された型
この場合、参照は結果オブジェクト、またはその適切な基底クラスの部分オブジェクトにバインドされます。 |
(C++17以降) |
間接バインディング
直接バインディングが利用できない場合、間接バインディングが考慮されます。この場合、
T
は
U
に対して参照関連(reference-related)にはなりません。
T
または
U
がクラス型の場合、ユーザー定義変換は、
copy-initialization
のルールに従って考慮されます。対応する非参照のcopy-initializationが不適格である場合、プログラムは不適格です。変換関数の呼び出し結果は、非参照の
copy-initialization
で説明されているように、参照のdirect-initializationに使用されます。このdirect-initializationでは、ユーザー定義変換は考慮されません。
|
それ以外の場合、型
|
(C++17まで) |
|
それ以外の場合、
target
は「cv-unqualified
|
(C++17以降) |
const std::string& rs = "abc"; // rsはchar配列からコピー初期化された一時オブジェクトを参照 const double& rcd2 = 2; // rcd2は値2.0を持つ一時オブジェクトを参照 int i3 = 2; double&& rrd3 = i3; // rrd3は値2.0を持つ一時オブジェクトを参照
一時オブジェクトの生存期間
参照が一時オブジェクトまたはその部分オブジェクトに束縛される場合、一時オブジェクトの寿命は参照の寿命に合わせて延長されます( 一時オブジェクト寿命の例外 を参照)。ここで一時オブジェクトまたはその部分オブジェクトは、以下のいずれかの式によって示されます:
|
(until C++17) |
| (since C++17) |
- 括弧で囲まれた式 ( e ) 。ここで e はこれらの式のいずれか。
- 組み込み添字式 。形式は a [ n ] または n [ a ] 。ここで a は配列であり、これらの式のいずれか。
- クラスメンバアクセス式 。形式は e. m 。ここで e はこれらの式のいずれかであり、 m はオブジェクト型の非静的データメンバを指定。
- メンバへのポインタ演算 。形式は e. * mp 。ここで e はこれらの式のいずれかであり、 mp はデータメンバへのポインタ。
-
const_cast、static_cast、dynamic_cast、またはreinterpret_cast変換(ユーザー定義変換を含まない)。これらの式のいずれかを被演算子が指すオブジェクト、その完全オブジェクト、またはその部分オブジェクトを参照するglvalueに変換する(明示的キャスト式はこれらのキャストのシーケンスとして解釈される)。 - 条件演算子 。形式は cond ? e1 : e2 でglvalueとなるもの。ここで e1 または e2 はこれらの式のいずれか。
- 組み込みコンマ演算子 。形式は x, e でglvalueとなるもの。ここで 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 が解決されるまで、コンストラクタの initializer list において一時オブジェクトが参照メンバーに束縛されることが許可されており、その寿命はオブジェクトの存続期間ではなくコンストラクタが終了するまででした。このような初期化は CWG 1696 以降では不適格となりますが、多くのコンパイラは依然としてこれをサポートしています(顕著な例外はclangです)。
例
#include <sstream> #include <utility> struct S { int mi; const std::pair<int, int>& mp; // 参照メンバ }; void foo(int) {} struct A {}; struct B : A { int n; operator int&() { return n; } }; B bar() { return B(); } //int& bad_r; // エラー: 初期化子なし extern int& ext_r; // OK int main() { // 左辺値 int n = 1; int& r1 = n; // オブジェクトnへの左辺値参照 const int& cr(n); // 参照はよりcv修飾可能 volatile int& cv{n}; // 任意の初期化構文が使用可能 int& r2 = r1; // オブジェクトnへの別の左辺値参照 // int& bad = cr; // エラー: より少ないcv修飾 int& r3 = const_cast<int&>(cr); // const_castが必要 void (&rf)(int) = foo; // 関数への左辺値参照 int ar[3]; int (&ra)[3] = ar; // 配列への左辺値参照 B b; A& base_ref = b; // 基底部分オブジェクトへの参照 int& converted_ref = b; // 変換結果への参照 // 右辺値 // int& bad = 1; // エラー: 左辺値参照を右辺値に束縛できない const int& cref = 1; // 右辺値に束縛 int&& rref = 1; // 右辺値に束縛 const A& cref2 = bar(); // B一時オブジェクトのA部分オブジェクトへの参照 A&& rref2 = bar(); // 同上 int&& xref = static_cast<int&&>(n); // 直接nに束縛 // int&& copy_ref = n; // エラー: 左辺値に束縛できない double&& copy_ref = n; // 値1.0を持つ右辺値一時オブジェクトに束縛 // 一時オブジェクトの寿命に関する制限 // std::ostream& buf_ref = std::ostringstream() << 'a'; // ostringstream一時オブジェクトはoperator<<の左オペランドに束縛されたが、 // その寿命はセミコロンで終了したため、buf_refはダングリング参照となる S a {1, {2, 3}}; // 一時pair {2, 3}が参照メンバa.mpに束縛され、 // その寿命はオブジェクトaの寿命に一致するように延長される S* p = new S{1, {2, 3}}; // 一時pair {2, 3}が参照メンバp->mpに束縛されたが、 // その寿命はセミコロンで終了したため // p->mpはダングリング参照となる delete p; // 以下の変数に適用される[[maybe_unused]]を模倣: [](...){} ( 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 | 参照互換性が修飾変換を考慮していなかった | 考慮 |
| CWG 2481 | C++17 |
間接バインドにおける一時オブジェクト化の結果型に
cv修飾が追加されなかった |
追加 |
| CWG 2657 | C++17 |
直接バインドにおける一時オブジェクト化の結果型に
cv修飾が追加されなかった |
追加 |
| CWG 2801 | C++98 | 間接バインドで参照関連型が許可されていた | 禁止 |