Döngüler için menzil tabanlı olarak yönlendirme referansları kullanmanın avantajı nedir?


115

const auto&salt okunur işlemler yapmak istersem yeterli olur. Ancak, çarpıştım

for (auto&& e : v)  // v is non-const

son zamanlarda birkaç kez. Bu beni meraklandırıyor:

Bazı belirsiz köşe durumlarda, yönlendirme referanslarını kullanmanın auto&veya ile karşılaştırıldığında bazı performans faydaları olması mümkün müdür const auto&?

( shared_ptrbelirsiz köşe davaları için bir şüpheli)


Favorilerimde bulduğum iki örneği güncelle :

Temel türler üzerinde yineleme yaparken const başvurusu kullanmanın herhangi bir dezavantajı var mı?
Aralık tabanlı bir for döngüsü kullanarak bir haritanın değerleri üzerinde kolayca yineleme yapabilir miyim?

Lütfen şu soruya odaklanın: neden döngüler için aralık tabanlı otomatik && kullanmak isteyeyim?


4
Gerçekten "sık sık" görüyor musun ?
Orbit'te Hafiflik Yarışları

2
Sorunuzda, gördüğünüz yerin ne kadar "çılgınca" olduğunu ölçmek için yeterli bağlam olduğundan emin değilim.
Orbit'te Hafiflik Yarışları

2
@LightnessRacesinOrbit Uzun lafın kısası: Neden auto&&döngüler için menzil tabanlı kullanmak isteyeyim ?
Ali

Yanıtlar:


94

Görebildiğim tek avantaj, sıra yineleyicinin bir proxy referansı döndürdüğü ve bu referans üzerinde const olmayan bir şekilde işlem yapmanız gerektiğidir. Örneğin şunları düşünün:

#include <vector>

int main()
{
    std::vector<bool> v(10);
    for (auto& e : v)
        e = true;
}

Bu derlenmez çünkü öğeden vector<bool>::referencedöndürülen rvalue , iteratorconst olmayan bir lvalue referansına bağlanmaz. Ama bu işe yarayacak:

#include <vector>

int main()
{
    std::vector<bool> v(10);
    for (auto&& e : v)
        e = true;
}

Tüm söylenenler, böyle bir kullanım durumunu tatmin etmeniz gerektiğini bilmediğiniz sürece bu şekilde kodlamazdım. Yani bunu karşılıksız yapmazdım çünkü bu , insanların ne yaptığını merak etmesine neden oluyor. Ve eğer bunu yaparsam, neden diye bir yorum eklemekten zarar gelmez:

#include <vector>

int main()
{
    std::vector<bool> v(10);
    // using auto&& so that I can handle the rvalue reference
    //   returned for the vector<bool> case
    for (auto&& e : v)
        e = true;
}

Düzenle

Bu son durumum gerçekten mantıklı olması için bir şablon olmalı. Döngünün her zaman bir proxy referansını işlediğini biliyorsanız, o zaman autoda işe yarayacaktır auto&&. Ancak döngü bazen vekil olmayan referansları ve bazen vekil referansları işlerken, o zaman auto&&tercih edilen çözüm olacağını düşünüyorum .


3
Öte yandan, açık bir dezavantaj yok , değil mi? (Potansiyel olarak kafa karıştırıcı insanların yanı sıra, şahsen bahsetmeye değer olduğunu düşünmüyorum.)
ildjarn

7
İnsanların gereksiz yere kafasını karıştıran kod yazmak için kullanılan bir başka terim de kafası karışmış kod yazmaktır. Kodunuzu olabildiğince basit hale getirmek en iyisidir, ancak daha basit değildir. Bu, hatayı geri saymaya yardımcı olacaktır. Bununla birlikte, && daha tanıdık hale geldikçe, bundan 5 yıl sonra insanlar bir otomatik && deyim beklemeye başlayacaklar (aslında zararı olmadığını varsayarak). Bunun olup olmayacağını bilmiyorum. Ancak basit, bakanın gözündedir ve kendinizden daha fazlası için yazıyorsanız, okuyucularınızı hesaba katın.
Howard Hinnant

7
const auto&Derleyicinin sıradaki öğeleri yanlışlıkla değiştirmediğimi kontrol etmeme yardım etmesini istediğimde tercih ederim .
Howard Hinnant

31
Şahsen auto&&, dizinin öğelerini değiştirmem gereken genel kodda kullanmayı seviyorum . Yapmazsam, sadık kalırım auto const&.
Xeo

7
@Xeo: +1 C ++ gelişmeye devam eden sizin gibi meraklılar yüzünden, sürekli olarak deneyler yapıyor ve daha iyi şeyler yapmanın yollarını zorluyor. Teşekkür ederim. :-)
Howard Hinnant

26

Kullanılması auto&&veya evrensel referanslar aralık tabanlı bir ile for-loop ne olsun yakaladığı avantajı vardır. Yineleyicilerin çoğu tür için muhtemelen ya bir alırsınız T&ya da T const&bazı tür T. İlginç bir durum, bir yineleyicinin başvurudan kaldırılmasının geçici bir sonuç verdiği durumdur: C ++ 2011, rahat gereksinimler aldı ve yineleyicilerin bir değer vermesi zorunlu değildir. Evrensel referansların kullanımı, aşağıdaki argüman iletimi ile eşleşir std::for_each():

template <typename InIt, typename F>
F std::for_each(InIt it, InIt end, F f) {
    for (; it != end; ++it) {
        f(*it); // <---------------------- here
    }
    return f;
}

İşlevi nesnesi ftedavi edebilir T&, T const&ve Tfarklı. Aralık temelli bir fordöngünün gövdesi neden farklı olmalıdır? Elbette, türü evrensel referansları kullanarak çıkarmış olmanın avantajlarından yararlanmak için bunları uygun şekilde iletmeniz gerekir:

for (auto&& x: range) {
    f(std::forward<decltype(x)>(x));
}

Elbette kullanmak, std::forward()taşınacak döndürülen değerleri kabul ettiğiniz anlamına gelir. Bunun gibi nesnelerin şablon dışı kodda çok anlamlı olup olmadığını bilmiyorum (henüz?). Evrensel referansları kullanmanın, Derleyiciye Doğru Şeyi yapması için daha fazla bilgi sunabileceğini hayal edebiliyorum. Şablonlu kodda, nesnelere ne olması gerektiğine dair herhangi bir karar vermenin dışında kalır.


9

Neredeyse her zaman kullanırım auto&&. Mecbur olmadığın halde neden bir uç durum tarafından ısırılasın? Yazması da daha kısa ve ben onu daha şeffaf buluyorum. Kullandığınızda , her seferinde bunun tam olarak auto&& xolduğunu bilirsiniz .x*it


29
Benim sorunum, eğer constyeterliyse , pes etmen . Soru, ısırılabileceğim köşe vakalarını soruyor. Dietmar veya Howard'ın henüz bahsetmediği köşe vakaları nelerdir? auto&&const auto&
Ali

2
Burada eğer Isırılmayın bir yolu yapmak kullanımını auto&&. Yakaladığınız tür döngünün gövdesine taşınmalı (örneğin) ve bir const&türe dönüşmesi için daha sonraki bir tarihe değiştirilirse , kodunuz sessizce çalışmaya devam edecek, ancak hareketleriniz kopyalar olacaktır. Bu kod çok yanıltıcı olacaktır. Bununla birlikte, türü açıkça bir r değeri referansı olarak belirtirseniz, konteyner türünü değiştiren kişi bir derleme hatası alır çünkü bu nesnelerin gerçekten taşınmasını ve kopyalanmamasını gerçekten istediniz ...
cyberbisson

@cyberbisson Şuna az önce rastladım: türü açıkça belirtmeden bir rvalue referansını nasıl uygulayabilirim? Gibi bir şey for (std::decay_t<decltype(*v.begin())>&& e: v)? Sanırım daha iyi bir yol var ...
Jerry Ma

@JerryMa Size ne olduğuna bağlıdır do türü hakkında biliyorum. Yukarıda bahsedildiği gibi, auto&&"evrensel bir referans" verecek, bu nedenle bundan başka herhangi bir şey size daha spesifik bir tür kazandıracaktır. Eğer a volduğu varsayılırsa vector, yapabilirsin decltype(v)::value_type&&, operator*bir yineleyici türünün sonucunu alarak bunu istediğini varsayıyorum . decltype(begin(v))::value_type&&Konteyner yerine yineleyicinin türlerini de kontrol edebilirsiniz. Tür hakkında bu kadar az auto&&
içgörümüz varsa, bununla
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.