参照の初期化 (C++ のみ)

参照を初期化するときには、その参照をオブジェクトにバインドします。 このときのオブジェクトは、必ずしも初期化指定子の式によって指定されるオブジェクトではありません。

参照は初期化されると、別のオブジェクトを参照するように変更することはできません。次に例を示します。
int num1 = 10;
int num2 = 20;

int &RefOne = num1;          // valid
int &RefOne = num2;          // error, two definitions of RefOne
RefOne = num2;               // assign num2 to num1
int &RefTwo;                 // error, uninitialized reference
int &RefTwo = num2;          // valid

参照の初期化は、参照への割り当てと同じではないことに注意してください。初期化は、実際の参照に対する別名であるオブジェクトに参照をバインドすることによって、実際の参照で動作します。割り当ては、 参照されるオブジェクトでの参照を通して動作します。

次の場合には、初期化指定子を使用せずに参照を宣言できます。
  • パラメーター宣言で使用する場合
  • 関数呼び出しの戻りの型の宣言内
  • クラス・メンバーのクラス宣言での宣言内
  • extern 指定子を明示的に使用する場合

参照バインディング

T および U が 2 つの型であるとします。トップレベルの cv 修飾子が無視された結果、TU と同じ型になるか U の基底クラスになる場合は、TU参照関連 があります。

例 1

typedef int t1;
typedef const int t2;
この例では、t1t2 に参照関連があります。

T および U に参照関連があり、T が 少なくとも U と同等の cv 修飾を持つ場合、T には U との 参照の互換性 があります。例 1 において、t1 には t2 との 参照の互換性がありませんが、t2 には t1 との参照の互換性があります。

T に対する左辺値 参照 r が型 U の式 e によって 初期化され、TU との参照の互換性がある場合は、 参照 re または e の基底クラス・サブオブジェクトに 直接バインドできます (ただし、T がアクセス可能であり、U の あいまいな基底クラスでもないとします)。

例 2

int a = 1;
const int& ra = a;

struct A {};
struct B: A {} b;

A& rb = b;
この例において、const int 型は int 型との参照の互換性があるため、ra は直接 a にバインドできます。 構造体 A には構造体 B との参照関連があるため、rb は直接 b にバインドできます。
T に対する左辺値参照 r を型 U の式 e で初期化する場合、以下の条件が満たされれば、re または e の基底クラスの変換の左辺値結果にバインドできます。この場合、変換関数は多重定義の解決によって選択されます。
  • U はクラス型です。
  • TU との参照関連がない。
  • e を型 S の左辺値に変換でき、かつ TS との参照の互換性がある。

例 3

struct A {
   operator int&(); 
};

const int& x= A();  
この例において、構造体 A はクラス型であり、const int 型には構造体 A との参照関連がありません。 しかし、A は型 int の左辺値に変換でき、const int には int との参照の互換性があります。したがって、型 const int の参照 xA() の変換結果にバインドできます。
デフォルトでは、コンパイラーは非定数または揮発性の左辺値参照を右辺値にバインドできません。

例 4

int& a = 2; // error
const int& b = 1; // ok
この例において、変数 a は const でない左辺値参照です。コンパイラーは、右辺値の式 2 で初期化された一時変数に a をバインドすることができず、エラー・メッセージを出します。変数 b は volatile でない const の左辺値参照であり、 右辺値の式 1 で初期化される一時変数で初期化できます。
IBM 拡張
-qlanglvl=compatrvaluebinding オプションを指定すると、コンパイラーは非定数または揮発性の左辺値参照を初期化指定子が必須ではないユーザー定義型の右辺値にバインドできます。このオプションのデフォルト値は、-qlanglvl=nocompatrvaluebinding です。このコンパイラーの動作は、非定数参照または揮発性の左辺値参照の右辺値へのバインドを許可していない、右辺値参照機能と競合します。 両方の機能を有効にした場合、コンパイラーはエラー・メッセージを発行します。右辺値参照機能について詳しくは、右辺値参照の使用 (C++11)を参照してください。
注:
  • 非定数または揮発性の左辺値参照を組み込み型の右辺値にバインドすることはできません。
  • クラス・メンバーである非定数または揮発性の左辺値参照を右辺値にバインドすることはできません。
IBM 拡張
C++11
U の式 e が以下のいずれかの値カテゴリーに属しているとします。
  • xvalue
  • クラス prvalue
  • 配列 prvalue
  • 関数左辺値
T に対する右辺値参照または volatile でない const の左辺値参照 r を式 e で 初期化する場合に、TU との参照の互換性がある場合は、参照 r を式 e で初期化できるほか、直接 e または e の基底クラス・サブオブジェクトにバインドすることができます (ただし、T がアクセス可能であり、U の あいまいな基底クラスでもないとします)。

例 5

int& func1();
int& (&&rf1)()=func1;

int&& func2();
int&& rf2 = func2();

struct A{
   int arr[5];
};
int(&&ar_ref)[5] = A().arr;
A&& a_ref = A();
この例において、rf1rf2ar_ref、および a_ref はいずれも右辺値参照です。rf1 は 関数左辺値 func1 にバインドされ、rf2 は呼び出し func2() の xvalue 結果バインドされ、ar_ref は配列 prvalue A().arr にバインドされ、a_ref はクラス prvalue A() にバインドされています。
r が、型 T に対する右辺値参照または volatile でない const の左辺値参照であり、その r を型 U の式 e で初期化するとします。 以下の条件が満たされる場合は、re または e の基底クラスの変換結果にバインドできます。この場合、変換関数は多重定義の解決によって選択されます。
  • U はクラス型です。
  • TU との参照関連がない。
  • eS xvalue、クラス prvalue、または関数左辺値型に変換でき、かつ TS との参照の互換性がある。

例 6

int i;
struct A {
   operator int&&() { 
     return static_cast<int&&>(i);
   }
 };

const int& x = A(); 

int main() { 
   assert(&x == &i); 
}  
この例において、構造体 A はクラス型であり、const int 型には構造体 A との参照関連がありません。 しかし、A を型 int の xvalue に変換することができ、const int には int との参照の互換性があるため、const int の参照 xA() で初期化し、変数 i にバインドすることができます。
以下のコンテキストでは、右辺値参照を左辺値で初期化できます。
  • 関数左辺値
  • 左辺値から変換された一時変数
  • クラス型である左辺値オブジェクトを対象とする変換関数の右辺値結果

例 7

int i = 1;
int&& a = 2; // ok
int&& b = i; // error
double&& c = i; // ok
この例において、右辺値参照 a は右辺値式 2 で初期化される一時変数にバインドできますが、右辺値参照 b は左辺値式 i にバインドできません。右辺値参照 c は、変数 i から変換された一時値 1.0 にバインドできます。
C++11