Const için rvalue referanslarının herhangi bir kullanımı var mı?


Yanıtlar:


78

Bazen faydalıdırlar. Taslak C ++ 0x'in kendisi bunları birkaç yerde kullanır, örneğin:

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

Yukarıdaki iki aşırı yükleme, diğerinin ref(T&)ve cref(const T&)işlevlerin (aksi takdirde mümkün olan) değerlere bağlanmamasını sağlar.

Güncelleme

Ne yazık ki kamuya açık olmayan resmi standart N3290'ı kontrol ettim ve 20.8 Function nesnelerinde [function.objects] / p2 var:

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

Ardından, halka açık olan en son C ++ 11 taslağını kontrol ettim, N3485 ve 20.8 İşlev nesneleri [function.objects] / p2'de hala diyor ki:

template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

Cppreference'e bakıldığında, artık durum böyle değil gibi görünüyor. Herhangi bir fikriniz neden? Başka yerler const T&&kullanılıyor mu?
Pubby

60
Aynı kodu neden üç kez yanıtınıza dahil ettiniz? Çok uzun süre bir fark bulmaya çalıştım.
typ1232

9
@ typ1232: Gönderilen işlevlerin artık görünmediğine ilişkin yorumlardaki endişeler nedeniyle, yanıtını yanıtladıktan yaklaşık 2 yıl sonra güncelledim. İşlevlerin hala göründüğünü göstermek için N3290'dan ve en son taslak N3485'ten kopyalayıp yapıştırdım. O zamanlar aklımda kopyala / yapıştır özelliğini kullanmak, benimkinden daha fazla gözün bu imzalardaki bazı küçük değişiklikleri gözden kaçırmadığımı doğrulamasını sağlamanın en iyi yoluydu.
Howard Hinnant

1
@kevinarpe: "Diğer aşırı yüklemeler" (burada gösterilmemiştir, ancak standartta gösterilmektedir) ldeğer referansları alır ve silinmez. Burada gösterilen aşırı yükler, ldeğer referansları alan aşırı yüklemelere göre r değerleri için daha iyi eşleşir. Dolayısıyla, rvalue bağımsız değişkenleri burada gösterilen aşırı yüklemelere bağlanır ve sonra bu aşırı yüklemeler silindiğinden derleme zamanı hatasına neden olur.
Howard Hinnant

1
Kullanımı, const T&&birisinin aptalca formun açık şablon değiştirgelerini kullanmasını engeller ref<const A&>(...). Bu korkunç güçlü argüman değil, ama maliyeti const T&&üzerinde T&&oldukça az.
Howard Hinnant

6

Sabit bir değer referansı (için değil =delete) almanın semantiği şunu söylemek içindir:

  • ldeğerler için operasyonu desteklemiyoruz!
  • olsa bile, biz hala kopya biz gelmediğinden dolayı hareket geçti kaynağı veya hiçbir gerçek anlamı için olduğundan bunu "hareketli".

Aşağıdaki kullanım durumu , IMHO'nun const'a rvalue başvurusu için iyi bir kullanım örneği olabilirdi , ancak dil bu yaklaşımı benimsememeye karar verdi ( orijinal SO gönderisine bakın ).


Durum: ham işaretçiden akıllı işaretçi yapıcısı

Genellikle kullanımı tavsiye edilmektedir make_uniqueve make_shared, ancak her ikisi de unique_ptrve shared_ptrbir ham işaretçi yapılabilir. Her iki yapıcı da işaretçiyi değere göre alır ve kopyalar. Her ikisi de kurucuda kendilerine iletilen orijinal göstericinin devamlı kullanımına izin verir (yani: engelleme anlamında ).

Aşağıdaki kod, double free ile derler ve sonuçlanır :

int* ptr = new int(9);
std::unique_ptr<int> p { ptr };
// we forgot that ptr is already being managed
delete ptr;

Her ikisi de unique_ptrve shared_ptrilgili kurucuları ham göstericiyi sabit bir değer olarak almayı beklerse , yukarıdakileri önleyebilir , örneğin unique_ptr:

unique_ptr(T* const&& p) : ptr{p} {}

Bu durumda, yukarıdaki çift ​​serbest kod derlenmez, ancak aşağıdakiler olur:

std::unique_ptr<int> p1 { std::move(ptr) }; // more verbose: user moves ownership
std::unique_ptr<int> p2 { new int(7) };     // ok, rvalue

Not ptrpotansiyel hata tamamen gitmiş değil bu yüzden hala ondan sonra kullanılabilir, taşındı. Ancak, kullanıcının std::moveböyle bir hatayı çağırması gerekiyorsa şu ortak kural geçerlidir: taşınmış bir kaynağı kullanmayın.


Biri sorabilir: Tamam, ama neden T* const&& p ?

Nedeni basit, unique_ptr const işaretçisinden oluşturulmasına izin vermek . Sabit rvalue referansının hem ve hem de kabul ettiği için rvalue referansından daha genel olduğunu unutmayın . Böylece aşağıdakilere izin verebiliriz:constnon-const

int* const ptr = new int(9);
auto p = std::unique_ptr<int> { std::move(ptr) };

sadece rvalue başvurusu beklersek bu olmazdı (derleme hatası: const rvalue ile rvalue bağlanamaz ).


Her neyse, böyle bir şey önermek için artık çok geç. Ancak bu fikir, sabit'e rvalue referansının makul bir kullanımını sunar .


Ben de benzer fikirleri olan insanlar arasındaydım, ama ilerleyecek desteğim yoktu.
Red.Wave

"auto p = std :: unique_ptr {std :: move (ptr)};" "sınıf şablonu bağımsız değişken kesintisi başarısız" hatasıyla derlenmiyor. Bunun "unique_ptr <int>" olması gerektiğini düşünüyorum.
Zehui Lin

4

Bunlara izin verilir ve hatta bunlara göre sıralanan işlevler const, ancak belirtilen const nesnesinden hareket const Foo&&edemediğiniz için yararlı değildirler.


"Sıralı" sözle tam olarak ne demek istiyorsun? Aşırı yük çözümü ile ilgili bir şey sanırım?
fredoverflow

Verilen tür, sabit rvalue-ref alan bir hareket ctoruna sahipse, neden bir sabit rvalue-ref'den hareket edemiyorsunuz?
Fred Nurk

6
@FredOverflow, aşırı yük sıralaması şudur:const T&, T&, const T&&, T&&
Gene Bushuyev

2
@Fred: Kaynağı değiştirmeden nasıl hareket edersiniz?
fredoverflow

3
@Fred: Değişken veri üyeleri veya belki de bu varsayımsal tür için hareket etmek veri üyelerinin değiştirilmesini gerektirmez.
Fred Nurk

2

Std :: ref yanında , standart kitaplık aynı amaç için std :: as_const içinde const rvalue referansını da kullanır .

template <class T>
void as_const(const T&&) = delete;

Ayrıca, sarılmış değer alınırken std :: isteğe bağlı içinde dönüş değeri olarak kullanılır :

constexpr const T&& operator*() const&&;
constexpr const T&& value() const &&;

Yanı sıra std :: get :

template <class T, class... Types>
constexpr const T&& get(const std::variant<Types...>&& v);
template< class T, class... Types >
constexpr const T&& get(const tuple<Types...>&& t) noexcept;

Bu muhtemelen değer kategorisinin yanı sıra sarılmış değere erişilirken sarmalayıcının sabitliğini korumak içindir.

Bu, const rvalue ref nitelikli işlevlerin sarmalanmış nesnede çağrılıp çağrılamayacağı konusunda bir fark yaratır. Bununla birlikte, const rvalue ref nitelikli işlevler için herhangi bir kullanım bilmiyorum.


1

Bunun doğrudan yararlı olacağı bir durum düşünemiyorum, ancak dolaylı olarak kullanılabilir:

template<class T>
void f(T const &x) {
  cout << "lvalue";
}
template<class T>
void f(T &&x) {
  cout << "rvalue";
}

template<class T>
void g(T &x) {
  f(T());
}

template<class T>
void h(T const &x) {
  g(x);
}

T g t const, yani f 'nin x, t const && olup.

Bu bir comile hata büyük olasılıkla bu sonuçlar ise f (taşımak veya nesneyi kullanmaya çalıştığında), fakat f çok basit olduğu gibi (rvalue değiştirmeden bu SolDeğerler çağrıda olamaz böylece bir rvalue-ref sürebilir yukarıdaki örnek).

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.