Namespaces
Variants

Reference declaration

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

既存のオブジェクトまたは関数へのエイリアスである名前付き参照変数を宣言します。

目次

翻訳のポイント: - HTMLタグ、属性、数値はそのまま保持 - C++専門用語(Syntax、Lvalue references、Rvalue references、Forwarding references)は原文のまま - 一般的な技術用語は日本語に翻訳(Contents→目次、Reference collapsing→参照畳み込み など) - 技術文書として適切な専門的な表現を使用

構文

参照変数の宣言は、 宣言子 が以下の形式を持つ任意の単純宣言です

& attr  (オプション) declarator (1)
&& attr  (オプション) declarator (2) (C++11以降)
1) Lvalue reference declarator : 宣言 S & D ; は、 D decl-specifier-seq S によって決定される型への lvalue reference として宣言する。
2) Rvalue reference declarator : 宣言 S && D ; D rvalue reference として宣言し、その型は decl-specifier-seq S によって決定される。
declarator - 参照宣言子、 配列宣言子 、および ポインタ宣言子 を除く任意の 宣言子 (参照への参照、参照の配列、参照へのポインタは存在しない)
attr - (C++11以降) 属性 のリスト

参照は有効なオブジェクトまたは関数を参照するように初期化する必要があります: reference initialization を参照してください。

「(可能性としてcv修飾された) void への参照」型を形成することはできません。

参照型はトップレベルで cv修飾 することはできません。宣言においてそのための構文は存在せず、typedef名 または decltype 指定子、 (C++11以降) または 型テンプレートパラメータ に修飾子が追加された場合、それは無視されます。

参照はオブジェクトではありません。必ずしもストレージを占有するわけではありませんが、コンパイラが必要なセマンティクスを実装するためにストレージを割り当てる場合があります(例:参照型の非静的データメンバは通常、メモリアドレスを格納するために必要な量だけクラスのサイズを増加させます)。

参照はオブジェクトではないため、参照の配列、参照へのポインタ、参照への参照は存在しません:

int& a[3]; // エラー
int&* p;   // エラー
int& &r;   // エラー

参照の畳み込み

テンプレートやtypedefにおける型操作を通じて参照への参照を形成することが許可されており、その場合 参照の畳み込み 規則が適用されます:右辺値参照から右辺値参照への畳み込みは右辺値参照となり、その他の全ての組み合わせは左辺値参照を形成します:

typedef int&  lref;
typedef int&& rref;
int n;
lref&  r1 = n; // type of r1 is int&
lref&& r2 = n; // type of r2 is int&
rref&  r3 = n; // type of r3 is int&
rref&& r4 = 1; // type of r4 is int&&

(これは、 テンプレート引数推論 における特別な規則(関数テンプレートで T&& が使用される場合)と組み合わさり、 std::forward を可能にする規則を形成します。)

(C++11以降)

Lvalue参照

Lvalue参照は既存のオブジェクトのエイリアスとして使用できます(オプションで異なるcv修飾を付加可能):

#include <iostream>
#include <string>
int main()
{
    std::string s = "Ex";
    std::string& r1 = s;
    const std::string& r2 = s;
    r1 += "ample";           // sを変更
//  r2 += "!";               // エラー: const参照を通して変更できない
    std::cout << r2 << '\n'; // sを出力(現在は"Example"を保持)
}

これらは関数呼び出しにおける参照渡しのセマンティクスを実装するためにも使用できます:

#include <iostream>
#include <string>
void double_string(std::string& s)
{
    s += s; // 's' は main() の 'str' と同じオブジェクト
}
int main()
{
    std::string str = "Test";
    double_string(str);
    std::cout << str << '\n';
}

関数の戻り値型が左辺値参照の場合、関数呼び出し式は lvalue 式になります:

#include <iostream>
#include <string>
char& char_number(std::string& s, std::size_t n)
{
    return s.at(n); // string::at() returns a reference to char
}
int main()
{
    std::string str = "Test";
    char_number(str, 1) = 'a'; // the function call is lvalue, can be assigned to
    std::cout << str << '\n';
}

Rvalue参照

右辺値参照は一時オブジェクトの 寿命を延長 するために使用できます(左辺値参照からconstへの参照も一時オブジェクトの寿命を延長できますが、それらを通じて変更することはできません):

#include <iostream>
#include <string>
int main()
{
    std::string s1 = "Test";
//  std::string&& r1 = s1;           // エラー: 左辺値にバインドできない
    const std::string& r2 = s1 + s1; // OK: constへの左辺値参照は寿命を延長する
//  r2 += "Test";                    // エラー: const参照を通して変更できない
    std::string&& r3 = s1 + s1;      // OK: 右辺値参照は寿命を延長する
    r3 += "Test";                    // OK: 非const参照を通して変更できる
    std::cout << r3 << '\n';
}

さらに重要なことに、関数が右辺値参照と左辺値参照の両方の オーバーロード を持つ場合、右辺値参照のオーバーロードは右辺値(prvalueとxvalueの両方を含む)にバインドし、左辺値参照のオーバーロードは左辺値にバインドします:

#include <iostream>
#include <utility>
void f(int& x)
{
    std::cout << "lvalue reference overload f(" << x << ")\n";
}
void f(const int& x)
{
    std::cout << "lvalue reference to const overload f(" << x << ")\n";
}
void f(int&& x)
{
    std::cout << "rvalue reference overload f(" << x << ")\n";
}
int main()
{
    int i = 1;
    const int ci = 2;
    f(i);  // f(int&) を呼び出す
    f(ci); // f(const int&) を呼び出す
    f(3);  // f(int&&) を呼び出す
           // f(int&&) オーバーロードが提供されていない場合は f(const int&) を呼び出す
    f(std::move(i)); // f(int&&) を呼び出す
    // rvalue reference 変数は式内で使用されるとき lvalue となる
    int&& x = 1;
    f(x);            // f(int& x) を呼び出す
    f(std::move(x)); // f(int&& x) を呼び出す
}

これにより、適切な場合に move constructors move assignment 演算子、およびその他のmove対応関数(例: std::vector::push_back() )が自動的に選択されるようになります。

rvalue参照はxvalueに束縛できるため、非一時オブジェクトを参照することができます:

int i2 = 42;
int&& rri = std::move(i2); // i2に直接バインドする

これにより、不要になったスコープ内のオブジェクトから移動することが可能になります:

std::vector<int> v{1, 2, 3, 4, 5};
std::vector<int> v2(std::move(v)); // v に右辺値参照をバインドする
assert(v.empty());

転送参照 (Forwarding references)

転送参照は、関数引数の値カテゴリを保持する特別な種類の参照であり、 転送 std::forward によって可能にします。転送参照は以下のいずれかです:

1) 関数テンプレートの関数パラメータで、同じ関数テンプレートのcv修飾されていない type template parameter への右辺値参照として宣言されている場合:
template<class T>
int f(T&& x)                      // xは転送参照
{
    return g(std::forward<T>(x)); // そのため転送可能
}
int main()
{
    int i;
    f(i); // 引数は左辺値、f<int&>(int&)を呼び出し、std::forward<int&>(x)は左辺値
    f(0); // 引数は右辺値、f<int>(int&&)を呼び出し、std::forward<int>(x)は右辺値
}
template<class T>
int g(const T&& x); // xは転送参照ではない: const TはCV修飾されていない型ではない
template<class T>
struct A
{
    template<class U>
    A(T&& x, U&& y, int* p); // xは転送参照ではない: Tはコンストラクタの
                             // 型テンプレートパラメータではないが、
                             // yは転送参照である
};
2) auto && 波括弧で囲まれた初期化子リストから推論される場合を除く または、クラステンプレートのテンプレートパラメータを表す場合で クラステンプレート引数推論 中である場合 (C++17以降) :
auto&& vec = foo();       // foo() は左辺値または右辺値の可能性があり、vec は転送参照です
auto i = std::begin(vec); // どちらの場合でも動作します
(*i)++;                   // どちらの場合でも動作します
g(std::forward<decltype(vec)>(vec)); // 値カテゴリを保持して転送します
for (auto&& x: f())
{
    // x は転送参照です。これはジェネリックコードで範囲ベースforループを使用する一般的な方法です
}
auto&& z = {1, 2, 3}; // 転送参照では*ありません*(初期化子リストの特別なケース)

関連項目 template argument deduction および std::forward .

(C++11以降)

ダングリング参照

参照は初期化時に常に有効なオブジェクトまたは関数を参照しますが、参照先オブジェクトの 寿命 が終了した後も参照がアクセス可能なままとなるプログラムを作成することは可能です( dangling )。

参照型の式 expr が与えられ、参照によって示されるオブジェクトまたは関数を target とする場合:

  • target へのポインタが expr の評価コンテキストにおいて 有効 である場合、結果は target を指す。
  • そうでない場合、動作は未定義である。
std::string& f()
{
    std::string s = "Example";
    return s; // sのスコープを抜ける:
              // デストラクタが呼び出され、ストレージが解放される
}
std::string& r = f(); // ダングリング参照
std::cout << r;       // 未定義動作: ダングリング参照からの読み取り
std::string s = f();  // 未定義動作: ダングリング参照からのコピー初期化

右辺値参照およびconstへの左辺値参照は一時オブジェクトの寿命を延長することに注意してください(規則と例外については Reference initialization を参照)。

参照先のオブジェクトが破棄された(例えば明示的なデストラクタ呼び出しによって)が、ストレージが解放されなかった場合、寿命外のオブジェクトへの参照は限定的な方法で使用でき、同じストレージでオブジェクトが再作成された場合は有効になる可能性があります(詳細は 寿命外アクセス を参照してください)。

型非アクセス可能参照

オブジェクトへの参照をバインドしようとした際、変換された初期化子が lvalue (C++11まで) glvalue (C++11以降) であり、そのオブジェクトが type-accessible でない場合、未定義動作が発生します:

char x alignas(int);
int& ir = *reinterpret_cast<int*>(&x); // 未定義動作:
                                       // 初期化子がcharオブジェクトを参照

呼び出し互換性のない参照

参照を関数にバインドしようとする際、変換された初期化子が lvalue (C++11まで) glvalue (C++11以降) であり、その型が関数の定義の型と call-compatible でない場合、未定義動作が発生します:

void f(int);
using F = void(float);
F& ir = *reinterpret_cast<F*>(&f); // 未定義動作:
                                   // 初期化子がvoid(int)関数を参照しています

注記

機能テストマクロ 規格 機能
__cpp_rvalue_references 200610L (C++11) Rvalue references

不具合報告

以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。

DR 適用バージョン 公開時の動作 正しい動作
CWG 453 C++98 参照がどのオブジェクトや関数に束縛できないか不明確だった 明確化された
CWG 1510 C++11 decltype のオペランドでCV修飾参照を形成できなかった 許可された
CWG 2550 C++98 パラメータが「 void への参照」型を持つことができた 禁止された
CWG 2933 C++98 ダングリング参照へのアクセス動作が不明確だった 明確化された

外部リンク

Thomas Becker, 2013 - C++ Rvalue References Explained
日本語訳:
Thomas Becker, 2013 - C++ Rvalue References Explained
翻訳内容: - HTMLタグ、属性、スタイルはそのまま保持 - ` `タグ内の「C++ Rvalue References Explained」はC++専門用語のため翻訳せず - 日付形式と名前はそのまま保持 - ハイフンとリンク構造は元のフォーマットを維持