C ++ std :: ref (T) ve T &? Arasındaki fark?


92

Bu programla ilgili bazı sorularım var:

#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;
}

Çıktı:

false

Bilmek istiyorum std::ref, bir nesnenin referansını döndürmezse, o zaman ne işe yarar ? Temel olarak, aşağıdakiler arasındaki fark nedir:

T x;
auto r = ref(x);

ve

T x;
T &y = x;

Ayrıca, bu farkın neden var olduğunu bilmek istiyorum. Neden ihtiyacımız var std::refveya std::reference_wrapperreferanslarımız olduğunda (yani T&)?



İpucu: x = y; Her iki durumda da yaparsanız ne olur ?
juanchopanza

2
Yinelenen bayrağıma (son yorum) ek olarak: Örneğin stackoverflow.com/questions/31270810/… ve stackoverflow.com/questions/26766939/…
anderas

2
@anderas, kullanışlılıkla ilgili değil, esas olarak farkla ilgili
CppNITR

@CppNITR O zaman yorumunuzdan birkaç saniye önce bağladığım sorulara bakın. Özellikle ikincisi kullanışlıdır.
anderas

Yanıtlar:


91

Well, refbir nesneye reference_wrapperbir referans tutmak için uygun türde bir nesne oluşturur. Bunun anlamı, başvurduğunuzda:

auto r = ref(x);

Bu reference_wrapper, x(ie T&) ' ye doğrudan bir referans değil, a döndürür . Bu reference_wrapper(yani r) onun yerine geçerlidir T&.

Bir reference_wrappersen bir taklit istediğinizde çok kullanışlıdır referencekopyalanabilir bir nesnenin (bu hem kopyalama constructible ve kopya devredilemeyen ).

C ++, referans (diyelim oluşturduktan sonra ybir nesne (söylemek) x, sonra) yve xaynı paylaşan taban adresi . Ayrıca, ybaşka herhangi bir nesneye atıfta bulunamaz. Ayrıca bir referans dizisi oluşturamazsınız, yani bunun gibi kod bir hata verir:

#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;
}

Ancak bu yasaldır:

#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
*/

Sorununuz hakkında konuşmak cout << is_same<T&,decltype(r)>::value;, çözüm şudur:

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

Size bir program göstereyim:

#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
*/

Buraya bakın, üç şeyi öğreniyoruz:

  1. Bir reference_wrappernesne (burada r) ile mümkün olmayan bir referans dizisi oluşturmak için kullanılabilir T&.

  2. raslında gerçek bir referans gibi davranır ( r.get()=70değerinin nasıl değiştiğine bakın y).

  3. rile aynı değil T&ama r.get(). Bu, rie'nin T&adından da anlaşılacağı gibi bir referansın etrafına sarıldığı anlamına gelir T&.

Umarım bu cevap şüphelerinizi açıklamak için fazlasıyla yeterlidir.


3
1: Hayır, bir reference_wrapperedilebilir yeniden , ama bu "birden fazla nesnelere tutma başvuru" olamaz. 2/3: Nerede .get()uygun olduğu konusunda adil nokta - ancak eksiz r , dönüşümünün açık bir şekilde çağrılabildiği T&durumlarda olduğu gibi kullanılabilir - bu nedenle kodunuzda birkaç tane de dahil olmak üzere birçok durumda aramanıza gerek yoktur (okunması zordur) boşluk eksikliği nedeniyle). roperator.get()
underscore_d

@underscore_d reference_wrapperemin değilseniz bir dizi referans tutabilir, o zaman kendiniz deneyebilirsiniz. Artı .get(), reference_wrappertuttuğu nesnenin değerini değiştirmek istediğinizde kullanılır, yani r=70yasa dışıdır, bu nedenle kullanmanız gerekir r.get()=70. Kendinizi deneyin !!!!!!
Ankit Acharya

1
Pek sayılmaz. Öncelikle doğru ifadeler kullanalım. Bir diziye referans tutan bir reference_wrapper'ı gösteriyorsunuz - "birden fazla nesneye referans" içeren bir reference_wrapper'ı değil . Sarıcı yalnızca bir referans içerir. İkinci olarak, bir diziye yerel bir referans bulabilirim - etrafındaki (parantezleri) unutmadığınızdan emin misiniz int a[4]{1, 2, 3, 4}; int (&b)[4] = a;? reference_wrapper yerli beri burada özel değil T& does işi.
underscore_d

1
@AnkitAcharya Evet :-) ama kesin olmak gerekirse, etkili sonuç bir yana, kendisi yalnızca bir nesneyi ifade ediyor. Her neyse, tabii ki haklısınız, normal bir referansın aksine, wrapperkutu bir konteynere girebilir. Bu kullanışlı, ancak bence insanlar bunu gerçekte olduğundan daha gelişmiş olarak yanlış yorumluyor. Bir dizi 'referans' istersem, genellikle aracıyı atlarım vector<Item *>, bu da wrapperkaynama noktasıdır ... ve umarım anti-işaretçiler beni bulmaz. İkna edici kullanım durumları farklı ve daha karmaşıktır.
underscore_d

1
"Bir reference_wrapper, kopyalanabilen bir nesnenin referansını taklit etmek istediğinizde çok kullanışlıdır." Ama bu bir referansın amacını bozmaz mı? Sen asıl şeyi kastediyorsun. Referans budur. Kopyalanabilen bir referans anlamsız görünüyor, çünkü kopyalamak istiyorsanız, o zaman ilk etapta bir referans istemezsiniz, sadece orijinal nesneyi kopyalamanız gerekir. İlk etapta var olması gerekmeyen bir problemi çözmek gereksiz bir karmaşıklık katmanı gibi görünüyor.
stu

53

std::reference_wrapper değere göre geçiş bağlamlarında nesneleri referansla geçirebilmesi için standart tesisler tarafından tanınmaktadır.

Örneğin, std::bindiçinde alabilir std::ref()daha sonra tekrar bir referans haline, bir şeye değeriyle iletmek ve açar.

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();
}

Bu pasajın çıktıları:

10
20

Değeri , başlatıldığı noktada idepolandı (değere göre alındı) f1, ancak f2bir std::reference_wrapperdeğeri korudu ve bu nedenle bir int&.


2
@CppNITR tabii! Küçük bir demo hazırlamak için bana biraz zaman verin :)
Quentin

1
Peki ya T & & ref (T)
CppNITR

3
@CppNITR std::ref(T)bir std::reference_wrapper. Bu, sarılmış bir göstericiden biraz daha fazlasıdır, ancak kütüphane tarafından "hey, referans olmam gerekiyor! Lütfen beni dolaştırmayı bitirdikten sonra beni tekrar bir taneye çevirin" olarak tanınır.
Quentin

38

Bir referans ( T&veya T&&), C ++ dilinde özel bir öğedir. Bir nesneyi referans olarak değiştirmeye izin verir ve dilde özel kullanım durumları vardır. Örneğin, referansları tutmak için standart bir kap oluşturamazsınız: vector<T&>biçimsizdir ve bir derleme hatası oluşturur.

std::reference_wrapperÖte yandan A , bir referans tutabilen bir C ++ nesnesidir. Hal böyle olunca da standart konteynerlerde kullanabilirsiniz.

std::refstd::reference_wrapperargümanında a döndüren standart bir işlevdir . Aynı fikirde, const referansına std::crefgeri döner std::reference_wrapper.

A'nın ilginç bir özelliği std::reference_wrapper, bir operator T& () const noexcept;. Bu , gerçek bir nesne olsa bile , tuttuğu referansa otomatik olarak dönüştürülebileceği anlamına gelir . Yani:

  • atanabilir bir nesne olduğu için, konteynerlerde veya referanslara izin verilmeyen diğer durumlarda kullanılabilir
  • sayesinde operator T& () const noexcept;referans kullanabileceğiniz her yerde kullanılabilir, çünkü otomatik olarak referansa dönüştürülecektir.

1
operator T& ()diğer 2 yanıttan bahsedilmediği için esas olarak oy verildi .
metablaster
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.