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


92

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

#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::refstd::reference_wrapper、または参照がある場合(つまりT&)?



ヒント:x = y; 両方の場合に行うとどうなりますか?
juanchopanza 2015年

2
私の重複フラグ(最後のコメント)に加えて:例えばstackoverflow.com/questions/31270810/…stackoverflow.com/questions/26766939/…を
anderas 2015年

2
@anderasそれは有用性についてではなく、主に違いについてです
CppNITR 2015年

@CppNITR次に、コメントの数秒前にリンクした質問を参照してください。特に2つ目は便利です。
anderas 2015年

回答:


91

まあref、適切なのオブジェクト作成reference_wrapperオブジェクトへの参照を保持するタイプ。つまり、申請するとき:

auto r = ref(x);

これはareference_wrapperを返し、x(ie T&)への直接参照ではありません。これreference_wrapper(つまりr)は代わりにを保持しますT&

Areference_wrapperは、コピー可能なreferenceオブジェクトのをエミュレートする場合に非常に便利です(コピー構築可能コピー割り当て可能の両方です)。

C ++では、yオブジェクト(say x)への参照(say )を作成したら、同じベースアドレス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()=70の値がどのように変更されたかを参照してくださいy)。

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

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


3
1:いいえ、areference_wrapper再割り当てできますが、「複数のオブジェクトへの参照を保持」することはできません。2/3:どこ.get()が適切かについての公平なポイント-ただし、サフィックスなしr 、の変換を明確に呼び出すことができるT&場合と同じように使用できます-したがって、コード内のいくつかを含め、多くの場合、呼び出す必要はありません(読みにくい)スペース不足のため)。roperator.get()
underscore_d 2015

@underscore_dreference_wrapperは、不明な場合は参照の配列を保持でき、自分で試すことができます。Plus.get()は、保持しているオブジェクトの値を変更する場合に使用されます。reference_wrapperつまりr=70、違法であるため、を使用する必要がありますr.get()=70。自分で試してみてください!!!!!!
Ankit Acharya 2015

1
あんまり。まず、正確な表現を使用しましょう。配列への参照を保持するreference_wrapperを表示しています。それ自体に「複数のオブジェクトへの参照」が含まれているreference_wrapperではありません。ラッパーは1つの参照のみを保持します。第二に、配列へのネイティブ参照を問題なく取得できます-周りの(括弧)を忘れていないだけint a[4]{1, 2, 3, 4}; int (&b)[4] = a;ですか?ネイティブT& 機能するため、reference_wrapperはここでは特別ではありません。
underscore_d 2015

1
@AnkitAcharyaはい:-)しかし、正確で効果的な結果はさておき、それ自体は1つのオブジェクトのみを参照します。とにかく、もちろん、通常の参照とは異なり、wrapperはコンテナに入れることができます。これは便利ですが、人々はこれを実際よりも進んだものと誤解していると思います。'refs'の配列が必要な場合は、通常、仲介者をスキップしますvector<Item *>。これは、wrapper要約すると...そしてアンチポインターの純粋主義者が私を見つけられないことを願っています。その説得力のあるユースケースは異なり、より複雑です。
underscore_d 2015

1
「reference_wrapperは、コピー可能なオブジェクトの参照をエミュレートする場合に非常に便利です。」しかし、それは参照の目的を無効にしませんか?あなたは実際のものを参照しています。それがリファレンスです。コピーできる参照は無意味に思えます。コピーしたい場合、そもそも参照は必要ないので、元のオブジェクトをコピーするだけでよいからです。そもそも存在する必要のない問題を解決するために、不必要な複雑さの層のように思えます。
stu 2017年

53

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

の値は、初期化if1れた時点でに格納されています(値によって取得されています)が、値によってf2保持std::reference_wrapperされているため、を取得したように動作しint&ます。


2
@CppNITR確かに!小さなデモを組み立てる時間をください:)
Quentin

1
T&とref(T)の違いはどう
ですか

3
@CppNITRstd::ref(T)はを返しますstd::reference_wrapper。これはラップされたポインタにすぎませんが、ライブラリによって「ねえ、私は参照になるはずです!私を回し終えたら、私を1つに戻してください」と認識されます。
クエンティン

38

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

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

std::refは、std::reference_wrapper引数にaを返す標準関数です。同じ考え方で、std::crefリターンstd::reference_wrapper、const参照に。

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

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

1
operator T& ()に他の2つの回答が言及しなかったという理由で賛成した。
メタブラスター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.