Namespaces
Variants

Reference initialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

オブジェクトへの参照をバインドします。

目次

構文

非リスト初期化
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++コード構文は翻訳対象外のため、元の形式を保持しています。HTMLタグ、属性、 タグ内のテキスト、C++固有の用語はすべて原文のまま維持されています。
指定子リスト初期化 (C++20以降)
T  & 参照 = {. 識別子1 = 引数1 , . 識別子2 { 引数2 } ... };

T  & 参照 {. 識別子1 = 引数1 , . 識別子2 { 引数2 } ... };

(1)
T  && 参照 = {. 識別子1 = 引数1 , . 識別子2 { 引数2 } ... };

T  && 参照 {. 識別子1 = 引数1 , . 識別子2 { 引数2 } ... };

(2)
関数参照引数 ({. 識別子1 = 引数1 , . 識別子2 { 引数2 } ... }); (3)

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

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

1) 名前付き lvalue reference 変数が初期化子付きで宣言された場合。
2) 名前付き rvalue reference 変数が初期化子付きで宣言された場合。
3) 関数呼び出し式において、関数パラメータが参照型を持つ場合。
4) return文において、関数が参照型を返す場合。 return 返される参照が 一時オブジェクト式 の結果に束縛される場合、プログラムは不適格となる。 (C++26以降)
5) 参照型の non-static data member member initializer を使用して初期化される場合。

説明

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 は以下のいずれかのカテゴリの値です:
  • rvalue
(C++11まで)
  • non-bit-field xvalue
  • class prvalue
  • array prvalue
  • function lvalue
(C++11以降)
(C++17まで)
  • non-bit-field rvalue
  • function lvalue
(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の型を調整された型 P と見なします。

  • P target の型(すなわち U )から 調整 され、 T のcv修飾が追加されます。

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

(C++17以降)

それ以外の場合、以下のすべての条件が満たされるならば:

  • U はクラス型です。
  • T U と参照関連ではありません。
  • target は型 V の値 v に変換可能であり、 T V と参照互換性を持つ場合、ここで v は以下のいずれかのカテゴリに属します:
  • rvalue
(C++11まで)
  • xvalue
  • class prvalue
  • function lvalue
(C++11から)
(C++17まで)
  • rvalue
  • function lvalue
(C++17から)

その後、参照は変換結果、またはその適切な基底クラスの部分オブジェクトにバインドされます:

struct A {};
struct B : A {};
struct X { operator B(); } x;
const A& r = x; // 変換結果のAサブオブジェクトにバインド
B&& rrb = x;    // 変換結果に直接バインド

変換の結果がprvalueである場合、 一時オブジェクト化 が適用され、prvalueの型を調整された型 P と見なします。

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

この場合、参照は結果オブジェクト、またはその適切な基底クラスの部分オブジェクトにバインドされます。

(C++17以降)

間接バインディング

直接バインディングが利用できない場合、間接バインディングが考慮されます。この場合、 T U に対して参照関連(reference-related)にはなりません。

T または U がクラス型の場合、ユーザー定義変換は、 copy-initialization のルールに従って考慮されます。対応する非参照のcopy-initializationが不適格である場合、プログラムは不適格です。変換関数の呼び出し結果は、非参照の copy-initialization で説明されているように、参照のdirect-initializationに使用されます。このdirect-initializationでは、ユーザー定義変換は考慮されません。

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

(C++17まで)

それ以外の場合、 target は「cv-unqualified T 」型のprvalueに暗黙変換されます。一時オブジェクト実体化変換が適用され、prvalueの型を T と見なして、参照は結果オブジェクトにバインドされます。

(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 はこれらの式のいずれか。

この寿命ルールには以下の例外があります:

  • 関数の戻り値に束縛された一時オブジェクトは、 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以降)
翻訳のポイント: - HTMLタグ、属性、コードブロックはそのまま保持 - C++専門用語(aggregate、direct-initialization、list-initialization、full expressionなど)は適切に翻訳 - 技術的に正確な表現を維持 - 丸括弧(parens)と波括弧(braces)は日本語で一般的な「丸括弧」「波括弧」と翻訳 - コメント部分は英語のまま保持( タグ内のため)

一般的に、一時オブジェクトの寿命は「受け渡し」によってさらに延長されることはありません:一時オブジェクトがバインドされた参照変数またはデータメンバから初期化された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 間接バインドで参照関連型が許可されていた 禁止

関連項目