名前空間
変種
操作

参照初期化

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

参照をオブジェクトに束縛します。

目次

[編集] 構文

[編集] リスト初期化以外
T & ref = target ;

T & ref ( target );

(1)
T && ref = target ;

T && ref ( target );

(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 & ref { arg1, arg2, ... };

(1)
T && ref = { arg1, arg2, ... };

T && ref { arg1, arg2, ... };

(2)
func-refpar ({ arg1, arg2, ... }); (3)
[編集] 指定初期化リスト (C++20以降)
T & ref = {.des1 = arg1 , .des2 { arg2 } ... };

T & ref {.des1 = arg1 , .des2 { arg2 } ... };

(1)
T && ref = {.des1 = arg1 , .des2 { arg2 } ... };

T && ref {.des1 = arg1 , .des2 { arg2 } ... };

(2)
func-refpar ({.des1 = arg1 , .des2 { arg2 } ... }); (3)

Tへの参照は、T型のオブジェクト、T型の関数、またはTに暗黙的に変換可能なオブジェクトで初期化できます。初期化された参照は、別のオブジェクトを参照するように再設定(変更)することはできません。

参照は以下の状況で初期化されます。

1) 名前付きの左辺値参照変数が初期化子とともに宣言されたとき。
2) 名前付きの右辺値参照変数が初期化子とともに宣言されたとき。
3) 関数呼び出し式で、関数パラメータが参照型であるとき。
4) return文で、関数が参照型を返すとき。返される参照が一時オブジェクトの結果に束縛される場合、プログラムは不適格となります。(C++26以降)
5) 非静的メンバ変数が参照型で、メンバ初期化子を使用して初期化されたとき。

[編集] 説明

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つの型T1T2について

  • T1T2のcv修飾なしのバージョンをそれぞれU1U2とする。U1U2類似しているか、またはU1U2基底クラスである場合、T1T2と*参照関連*である。
  • T2へのポインタ」というprvalueが標準変換シーケンスを介して「T1へのポインタ」という型に変換できる場合、T1T2と*参照互換*である。

[編集] 初期化規則

参照初期化で通常のまたは指定された(C++20以降)リスト初期化が使用される場合、リスト初期化の規則に従います。

(C++11以降)

リスト初期化以外の場合、targetの型をUとして、参照はtargetに*直接束縛*されるか、またはtargetから変換されたT型の値に束縛されます。直接束縛が最初に考慮され、次に間接束縛が考慮されます。どちらの束縛も利用できない場合、プログラムは不適格です。

2つの型の参照互換関係が参照束縛の有効性を確立するために使用され、標準変換シーケンスが不適格となるすべてのケースにおいて、そのような束縛を必要とするプログラムは不適格です。

[編集] 直接束縛

以下のすべての条件が満たされる場合

  • 初期化される参照は左辺値参照である。
  • targetビットフィールドではない左辺値である。
  • TUと参照互換である。

この場合、参照は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はクラス型である。
  • TUと参照関連ではない。
  • targetV型の左辺値に変換でき、TVと参照互換である。

この場合、参照は変換の左辺値結果、またはその適切な基底クラスサブオブジェクトに束縛されます。

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まで)
  • ビットフィールドではないxvalue
  • クラスprvalue
  • 配列prvalue
  • 関数左辺値
(C++11以降)
(C++17まで)
  • ビットフィールドではない右辺値
  • 関数左辺値
(C++17以降)
  • TUと参照互換である。

この場合、参照は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の型は調整型Pとみなされます。

  • Pは、targetの型(すなわちU)から、Tのcv修飾子を加えて*調整*されます。

この場合、参照は結果オブジェクト、またはその適切な基底クラスサブオブジェクトに束縛されます。

(C++17以降)

そうでなく、以下のすべての条件が満たされる場合

  • Uはクラス型である。
  • TUと参照関連ではない。
  • targetV型の値vに変換でき、TVと参照互換である場合、ここでvは以下のいずれかのカテゴリの値である。
  • 右辺値
(C++11まで)
  • xvalue
  • クラスprvalue
  • 関数左辺値
(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の型は調整型Pとみなされます。

  • Pは、変換結果の型から、Tのcv修飾子を加えて*調整*されます。

この場合、参照は結果オブジェクト、またはその適切な基底クラスサブオブジェクトに束縛されます。

(C++17以降)

[編集] 間接束縛

直接束縛が利用できない場合、間接束縛が考慮されます。この場合、TUと参照関連ではありません。

TまたはUがクラス型の場合、ユーザー定義変換は、型Tのオブジェクトに対するコピー初期化の規則を使用して考慮されます。参照ではないコピー初期化が不適格となる場合、プログラムは不適格です。非参照コピー初期化で説明されている変換関数の呼び出し結果が、参照の直接初期化に使用されます。この直接初期化では、ユーザー定義変換は考慮されません。

それ以外の場合、型Tの一時オブジェクトが作成され、targetからコピー初期化されます。その後、参照は一時オブジェクトに束縛されます。

(C++17まで)

それ以外の場合、targetは「cv修飾なしT」という型のprvalueに暗黙的に変換されます。一時オブジェクト化変換が適用され、prvalueの型はTとみなされ、参照は結果オブジェクトに束縛されます。

(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

[編集] 一時オブジェクトの生存期間

参照が一時オブジェクトまたはそのサブオブジェクトに束縛される場合、一時オブジェクトの生存期間は参照の生存期間に一致するように延長されます(一時オブジェクトの生存期間の例外を参照)。ここで、一時オブジェクトまたはそのサブオブジェクトは、以下のいずれかの式によって示されます。

  • オブジェクト型のprvalue式。
(C++17まで)
(C++17以降)
  • これらの式のいずれかである括弧で囲まれた式 (e)
  • これらの式のいずれかである配列aおよび整数n組み込み添字演算子の形式 a[n] または n[a]
  • これらの式のいずれかである式eと、オブジェクト型の非静的メンバmを指定するクラスメンバアクセス演算子の形式 e.m
  • これらの式のいずれかである式eと、データメンバへのポインタであるmpを指定するポインタtoメンバ演算の形式 e.*mp
  • ユーザー定義変換のないconst_caststatic_castdynamic_cast、またはreinterpret_cast変換。これは、これらの式のいずれかをオペランドによって指定されるオブジェクト、またはその完全オブジェクトもしくはサブオブジェクトに変換します(明示的キャスト式はこれらのキャストのシーケンスとして解釈されます)。
  • 左辺値である形式 cond ? e1 : e2条件演算子。ここで、e1またはe2はこれらの式のいずれかです。
  • 左辺値である形式 x, e組み込みカンマ演算子。ここで、eはこれらの式のいずれかです。

この生存期間の規則には以下の例外があります。

  • return文の関数の戻り値に束縛された一時オブジェクトは延長されません。これは、return式の終了時に直ちに破棄されます。このようなreturn文は常にダングリング参照を返します。
(C++26まで)
  • 関数呼び出しの参照パラメータに束縛された一時オブジェクトは、その関数呼び出しを含むフル式が終了するまで存在します。関数が参照を返す場合、その参照がフル式より長く存続すると、ダングリング参照になります。
  • new式の初期化子内の参照に束縛された一時オブジェクトは、初期化されるオブジェクトの生存期間ではなく、そのnew式を含むフル式の終了まで存在します。初期化されるオブジェクトがフル式より長く存続する場合、その参照メンバはダングリング参照になります。
(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 間接束縛で参照関連型が許可されていました 禁止された

[編集] 関連項目

English 日本語 中文(简体) 中文(繁體)