web-dev-qa-db-ja.com

std :: ref(T)とT&のC ++の違いは?

このプログラムに関していくつか質問があります。

#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
template <typename T> void foo ( T x )
{
    auto r=ref(x);
    cout<<boolalpha;
    cout<<is_same<T&,decltype(r)>::value;
}
int main()
{
    int x=5;
    foo (x);
    return 0;
}

出力は次のとおりです。

false

std::refはオブジェクトの参照を返しませんが、何をしますか?基本的に、次の違いは何ですか?

T x;
auto r = ref(x);

そして

T x;
T &y = x;

また、なぜこの違いが存在するのか知りたいですか?なぜ必要なのかstd::ref または std::reference_wrapper参照がある場合(つまりT&)?

68
CppNITR

refは、適切な_reference_wrapper_型のオブジェクトを構築して、オブジェクトへの参照を保持します。つまり、適用する場合:

_auto r = ref(x);
_

これは、x(つまり_reference_wrapper_)への直接参照ではなく、_T&_を返します。この_reference_wrapper_(つまりr)は、代わりに_T&_を保持します。

_reference_wrapper_は、コピー可能なオブジェクトのreferenceをエミュレートするときに非常に便利です(両方ともcopy-constructiblecopy-assignable)。

C++では、オブジェクト(たとえばy)への参照(たとえばx)を作成したら、yxは同じものを共有しますベースアドレス。さらに、yは他のオブジェクトを参照できません。 参照の配列を作成することもできません。つまり、次のようなコードはエラーをスローします。

_#include <iostream>
using namespace std;

int main()
{
    int x=5, y=7, z=8;
    int& arr[] {x,y,z};    // error: declaration of 'arr' as array of references
    return 0;
}
_

ただし、これは合法です:

_#include <iostream>
#include <functional>  // for reference_wrapper
using namespace std;

int main()
{
    int x=5, y=7, z=8;
    reference_wrapper<int> arr[] {x,y,z};
    for (auto a: arr)
        cout << a << " ";
    return 0;
}
/* OUTPUT:
5 7 8
*/
_

cout << is_same<T&,decltype(r)>::value;の問題について言えば、解決策は次のとおりです。

_cout << is_same<T&,decltype(r.get())>::value;  // will yield true
_

プログラムを見せてください:

_#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;

int main()
{
    cout << boolalpha;
    int x=5, y=7;
    reference_wrapper<int> r=x;   // or auto r = ref(x);
    cout << is_same<int&, decltype(r.get())>::value << "\n";
    cout << (&x==&r.get()) << "\n";
    r=y;
    cout << (&y==&r.get()) << "\n";
    r.get()=70;
    cout << y;
    return 0;
}
/* Ouput:
true
true
true
70
*/
_

ここを参照して、3つのことを理解してください。

  1. _reference_wrapper_オブジェクト(ここではr)を使用して、参照の配列を作成できますが、これは_T&_では不可能でした。

  2. rは実際には実際の参照のように動作します(r.get()=70yの値を変更した方法を参照してください)。

  3. rは_T&_と同じではありませんが、r.get()は同じです。これは、rが_T&_を保持することを意味します。つまり、その名前が示すように、参照を囲むラッパー _T&_です。

この答えがあなたの疑問を説明するのに十分すぎることを願っています。

65
Ankit Acharya

_std::reference_wrapper_は、値渡しコンテキストで参照によってオブジェクトを渡すことができるように標準機能によって認識されます。

たとえば、_std::bind_はstd::ref()を何かに取り込み、値で送信し、後で参照に戻すことができます。

_void print(int i) {
    std::cout << i << '\n';
}

int main() {
    int i = 10;

    auto f1 = std::bind(print, i);
    auto f2 = std::bind(print, std::ref(i));

    i = 20;

    f1();
    f2();
}
_

このスニペットの出力:

_10
20
_

iの値は、初期化された時点で_f1_に格納(値によって取得)されていますが、_f2_は値によって_std::reference_wrapper_を保持しているため、動作します_int&_を取り込んだように。

42
Quentin

参照(_T&_または_T&&_)は、C++言語の特別な要素です。参照によってオブジェクトを操作することができ、言語に特別なユースケースがあります。たとえば、参照を保持する標準コンテナを作成することはできません。_vector<T&>_は不正な形式であり、コンパイルエラーを生成します。

一方、_std::reference_wrapper_は、参照を保持できるC++オブジェクトです。そのため、標準のコンテナで使用できます。

_std::ref_は、引数で_std::reference_wrapper_を返す標準関数です。同じアイデアで、_std::cref_はconst参照に_std::reference_wrapper_を返します。

_std::reference_wrapper_の興味深い特性の1つは、operator T& () const noexcept;があることです。つまり、真のオブジェクトであってもであっても、保持している参照に自動的に変換できます。そう:

  • コピーの割り当て可能なオブジェクトであるため、コンテナ内で、または参照が許可されていない他の場合に使用できます
  • operator T& () const noexcept;のおかげで、参照を使用できる場所であればどこでも使用できます。これは、参照に自動的に変換されるためです。
26
Serge Ballesta