TL; DR: Const referansı ile geçiş, C ++ 'da hala iyi bir fikirdir. Erken optimizasyon değil.
TL; DR2: Çoğu atasözü bunu yapana kadar mantıklı değil.
Amaç
Bu cevap sadece C ++ Temel Yönergeleri'nde (amon'un yorumunda ilk kez bahsedilmiştir) bağlantılı öğeyi biraz genişletmeye çalışır .
Bu cevap, programcıların çevrelerinde geniş çapta dolaşan çeşitli atasözleri, özellikle de çelişkili sonuçlar veya kanıtlar arasında uzlaşma sorunu nasıl düşünülüp uygulanacağı konusunu ele almaya çalışmamaktadır.
uygulanabilirlik
Bu yanıt yalnızca işlev çağrıları (aynı iş parçasındaki çıkarılamaz iç içe kapsamlar) için geçerlidir.
(Yan not.) Geçilebilir şeyler kapsamdan kaçabiliyorsa (örneğin, dış kapsamı potansiyel olarak aşan bir ömre sahipse), uygulamanın nesne ömrü yönetimi gereksinimini her şeyden önce karşılamak daha önemli hale gelir. Genellikle bu, akıllı işaretçiler gibi ömür boyu yönetim yeteneğine sahip referansların kullanılmasını gerektirir. Alternatif bir yönetici kullanıyor olabilir. Lambda'nın bir tür sökülebilir kapsam olduğunu unutmayın; lambda yakalamaları nesne kapsamına sahip gibi davranır. Bu nedenle, lambda yakalamalarına dikkat edin. Ayrıca lambda'nın kendisinin nasıl geçtiğine dikkat edin - kopya veya referans olarak.
Değere göre ne zaman geçilir?
Değerler için skaler olan iletişim bazında değişkenliği (ortak bir referans) için bir ihtiyaç vardır (bir makine yazmaçdaki uygun ve değeri semantik sahip standart ilkellerini) değeri ile geçmektedir.
Callee'nin bir nesnenin veya toplamın klonlanmasını gerektirdiği durumlarda, callee'nin kopyasının klonlanmış bir nesne ihtiyacını karşıladığı değere göre geçin.
Referans vb. İle ne zaman geçilir?
diğer tüm durumlar için, işaretçiler, referanslar, akıllı işaretçiler, tutamaklar (bkz: tutamak-vücut deyimi), vb.
Bellek ayak izinde yeterince büyük olan şeyler (kümeler, nesneler, diziler, veri yapıları), performans nedenleriyle her zaman referans yoluyla kolaylaştıracak şekilde tasarlanmalıdır. Bu tavsiye yüzlerce bayt veya daha fazla olduğunda kesinlikle geçerlidir. Bu tavsiye onlarca bayt olduğunda sınırdadır.
Olağandışı paradigmalar
Niyetle kopya ağır olan özel amaçlı programlama paradigmaları vardır. Örneğin, dize işleme, serileştirme, ağ iletişimi, yalıtım, üçüncü taraf kitaplıkların sarılması, paylaşılan bellek süreçler arası iletişim, vb. Bu uygulama alanlarında veya programlama paradigmalarında, veriler yapılardan yapılara kopyalanır veya bazen yeniden paketlenir bayt dizileri.
Optimizasyon düşünülmeden önce dil belirtiminin bu yanıtı nasıl etkilediği .
Alt TL; DR Bir referansın yayılması hiçbir kod çağırmamalıdır; const referansı ile geçmek bu kriteri karşılar. Ancak, diğer tüm diller bu kriteri zahmetsizce karşılar.
(Acemi C ++ programcılarına bu bölümü tamamen atlamaları önerilir.)
(Bu bölümün başlangıcı kısmen gnasher729'un cevabından esinlenmiştir. Ancak farklı bir sonuca varılmıştır.)
C ++, kullanıcı tanımlı kopya oluşturuculara ve atama işleçlerine izin verir.
(Bu, hem şaşırtıcı hem de pişman olan cesur bir seçimdi. Dil tasarımında bugünün kabul edilebilir normundan kesinlikle bir sapma.)
C ++ programcısı bir tanımlı tanımlamasa bile, C ++ derleyicisi bu tür yöntemleri dil ilkelerine göre oluşturmalı ve sonra başka kodların çalıştırılması gerekip gerekmediğini belirlemelidir memcpy
. Örneğin, bir class
/ struct
bir içeren std::vector
eleman bir kopyası-yapıcı olmayan önemsiz bir atama operatörü olması gerekiyor.
Diğer dillerde, kopya kurucuları ve nesne klonlaması önerilmez (nesnenin dil anlamına göre kesinlikle gerekli ve / veya uygulamanın anlambilimi hariç). Bu diller genellikle, kapsama dayalı sahiplik veya referans sayımı yerine erişilebilirliğe dayalı çöp toplama mekanizmasına sahip olacaktır.
C ++ (veya C) içinde bir başvuru veya işaretçi (sabit başvuru dahil) iletildiğinde, programcı adres değerinin yayılması dışında hiçbir özel kodun (kullanıcı tanımlı veya derleyici tarafından oluşturulan işlevler) yürütülmeyeceğinden emin olur. (başvuru veya işaretçi). Bu, C ++ programcılarının rahat bulduğu bir davranış netliğidir.
Bununla birlikte, arka plan, C ++ dilinin gereksiz bir şekilde karmaşık olmasıdır, öyle ki, bu davranış netliği nükleer serpinti bölgesinde bir vaha (hayatta kalabilen bir yaşam alanı) gibidir.
Daha fazla kutsama (veya hakaret) eklemek için C ++, kullanıcı tanımlı hareket operatörlerini (hareket oluşturucular ve hareket atama operatörleri) iyi performansla kolaylaştırmak için evrensel referanslar (r-değerleri) sunar. Bu, kopyalama ve derin klonlama ihtiyacını azaltarak, nesnelerin bir örnekten diğerine taşınması (aktarılması) için oldukça alakalı bir kullanım durumuna (aktarımına) yarar. Bununla birlikte, diğer dillerde, nesnelerin bu şekilde hareket etmesinden bahsetmek mantıksızdır.
(Konu dışı bölüm) "Hız İstiyor musunuz? Değere Göre Geç!" 2009 dolaylarında yazılmıştır.
Bu makale 2009 yılında yazılmıştır ve C ++ 'da r-değeri için tasarım gerekçesini açıklar. Bu makale önceki bölümdeki sonucum için geçerli bir karşı argüman sunuyor. Ancak, makalenin kod örneği ve performans iddiası uzun zamandır yalanlanmıştır.
Sub-TL; DR C ++ 'da r-değeri anlambiliminin tasarımı, Sort
örneğin bir işlev üzerinde şaşırtıcı derecede zarif bir kullanıcı tarafı anlambilimine izin verir . Bu şıklığın diğer dillerde modellenmesi (taklit edilmesi) imkansızdır.
Tüm veri yapısına bir sıralama işlevi uygulanır. Yukarıda belirtildiği gibi, çok fazla kopyalama yapılması yavaş olacaktır. Bir performans optimizasyonu olarak (pratik olarak önemlidir), bir sıralama işlevi C ++ dışında birkaç dilde yıkıcı olacak şekilde tasarlanmıştır. Yıkıcı, hedef veri yapısının sıralama hedefine ulaşmak için değiştirildiği anlamına gelir.
C ++ 'da, kullanıcı iki uygulamadan birini aramayı seçebilir: daha iyi performansa sahip yıkıcı olan veya girişi değiştirmeyen normal bir uygulama. (Şablon kısalık için kullanılmaz.)
/*caller specifically passes in input argument destructively*/
std::vector<T> my_sort(std::vector<T>&& input)
{
std::vector<T> result(std::move(input)); /* destructive move */
std::sort(result.begin(), result.end()); /* in-place sorting */
return result; /* return-value optimization (RVO) */
}
/*caller specifically passes in read-only argument*/
std::vector<T> my_sort(const std::vector<T>& input)
{
/* reuse destructive implementation by letting it work on a clone. */
/* Several things involved; e.g. expiring temporaries as r-value */
/* return-value optimization, etc. */
return my_sort(std::vector<T>(input));
}
/*caller can select which to call, by selecting r-value*/
std::vector<T> v1 = {...};
std::vector<T> v2 = my_sort(v1); /*non-destructive*/
std::vector<T> v3 = my_sort(std::move(v1)); /*v1 is gutted*/
Sıralama dışında, bu zarafet özyinelemeli bölümleme yoluyla bir dizide (başlangıçta ayrıştırılmamış) yıkıcı medyan bulma algoritmasının uygulanmasında da yararlıdır.
Ancak, çoğu dilde, dizilere yıkıcı bir sıralama algoritması uygulamak yerine, sınıflandırmaya dengeli bir ikili arama ağacı yaklaşımı uygulayacağını unutmayın. Bu nedenle, bu tekniğin pratik ilgisi göründüğü kadar yüksek değildir.
Derleyici optimizasyonu bu yanıtı nasıl etkiler?
Satır içi (ve ayrıca tüm program optimizasyonu / bağlantı zamanı optimizasyonu) birkaç işlev çağrısı seviyesine uygulandığında, derleyici veri akışını görebilir (bazen ayrıntılı olarak). Bu olduğunda, derleyici, bazıları bellekteki tüm nesnelerin oluşturulmasını ortadan kaldırabilen birçok optimizasyon uygulayabilir. Tipik olarak, bu durum geçerli olduğunda, parametrelerin değere göre mi yoksa sabit referansla mı iletildiği önemli değildir, çünkü derleyici tam olarak analiz edebilir.
Ancak, alt düzey işlev analiz dışında bir şeyi çağırırsa (örneğin, derleme dışındaki farklı bir kitaplıkta bulunan bir şey veya çok karmaşık bir çağrı grafiği), derleyici savunmacı bir şekilde en iyi duruma getirmelidir.
Bir makine kayıt değerinden daha büyük olan nesneler, açık bellek yükleme / saklama talimatları veya saygıdeğer memcpy
işleve çağrı ile kopyalanabilir . Bazı platformlarda, derleyici iki bellek konumu arasında hareket etmek için SIMD talimatları üretir, her komut onlarca bayt taşır (16 veya 32).
Ayrıntı düzeyi veya görsel karmaşa üzerine tartışma
C ++ programcıları buna alışkındır, yani bir programcı C ++ 'dan nefret etmediği sürece, kaynak kodda const referansı yazma veya okuma yükü korkunç değildir.
Maliyet-fayda analizleri daha önce birçok kez yapılmış olabilir. Belirtilmesi gereken herhangi bir bilimsel olup olmadığını bilmiyorum. Sanırım çoğu analiz bilimsel değil ya da tekrarlanamaz.
İşte hayal ettiğim şey (kanıt veya güvenilir referanslar olmadan) ...
- Evet, bu dilde yazılmış yazılımın performansını etkiler.
- Derleyiciler kodun amacını anlayabiliyorsa, bunu otomatikleştirmek için yeterince akıllı olabilir.
- Ne yazık ki, değişebilirliği tercih eden dillerde (işlevsel saflığın aksine), derleyici çoğu şeyi mutasyona uğramış olarak sınıflandırır, bu nedenle otomatikliğin sabit olarak çıkarılması çoğu şeyi const olmayan olarak reddeder
- Zihinsel yük insanlara bağlıdır; Bunu yüksek bir zihinsel yük olarak gören insanlar C ++ 'ı geçerli bir programlama dili olarak reddederdi.