Global lambdaları kullanmamak için bir sebep var mı?


89

Kendi içinde yakalamayan bir lambda kullanan bir fonksiyonumuz vardı, örneğin:

void foo() {
  auto bar = [](int a, int b){ return a + b; }

  // code using bar(x,y) a bunch of times
}

Şimdi lambda tarafından uygulanan işlevsellik başka bir yerde gerekli hale geldi, bu yüzden lambda'yı foo()küresel / ad alanı kapsamından kaldıracağım . Bir lambda olarak bırakabilirim, kopyala-yapıştır seçeneği yapabilir veya uygun bir işleve değiştirebilirim:

auto bar = [](int a, int b){ return a + b; } // option 1
int bar(int a, int b){ return a + b; } // option 2

void foo() {
  // code using bar(x,y) a bunch of times
}

Uygun bir işlevine değiştirme önemsiz, ama bana bir sebep mi var olup olmadığını merak yapılmış değil bir lambda olarak bırakmak için? "Normal" küresel işlevler yerine her yerde lambdaları kullanmamak için bir neden var mı?



Yanıtlar:


61

Küresel lambdaları kullanmamanın çok önemli bir nedeni var: çünkü bu normal değil.

C ++ 'ın düzenli işlev sözdizimi, C günlerinden beri olmuştur. Programcılar, yıllardır söz konusu sözdiziminin ne anlama geldiğini ve nasıl çalıştığını biliyorlar (itiraf etmek gerekirse, tüm işlev-işaretçi bozunma şey bazen deneyimli programcıları bile ısırır). Eğer "mutlak acemi" nin ötesinde herhangi bir beceri seviyesine sahip bir C ++ programcısı bir fonksiyon tanımı görürse, ne elde ettiklerini bilirler.

Küresel bir lambda tamamen farklı bir canavardır. Normal bir işlevden farklı davranışları vardır. Lambdalar nesnelerken işlevler değildir. Bir türü vardır, ancak bu tür işlevlerinin türünden farklıdır. Ve böylece.

Şimdi, diğer programcılarla iletişimde çıtayı yükselttin. Bir C ++ programcısı, bu işlevin ne yaptığını anlayacaklarsa lambdaları anlamalıdır. Ve evet, bu 2019, bu yüzden iyi bir C ++ programcısı bir lambda'nın neye benzediğine dair bir fikre sahip olmalı. Ama yine de daha yüksek bir çubuk.

Ve anlasalar bile, o programcının zihnindeki soru şu olacak ... bu kodun yazarı neden bu şekilde yazdı? Ve bu soru için iyi bir cevabınız yoksa (örneğin, Aralık özelleştirme noktalarında olduğu gibi aşırı yüklemeyi açıkça yasaklamak istediğiniz için), ortak mekanizmayı kullanmalısınız.

Uygun olan yerlerde yenilerine beklenen çözümleri tercih edin. Dikkatinizi çekmek için en az karmaşık yöntemi kullanın.


9
"C günlerinden beri" O zamandan önce arkadaşım
Orbit'te Hafiflik Yarışları

4
@LightnessRaces: Benim asıl amacım C ++ 'ın fonksiyon sözdiziminin C'den beri olduğunu ifade etmekti, bu yüzden birçok insan denetimin ne olduğunu biliyor.
Nicol Bolas

2
İlk cümle hariç tüm bu yanıtı kabul edin. "Bu normal değil" belirsiz ve belirsiz bir ifadedir. Belki de bunu "nadir ve kafa karıştırıcı" anlamında kastettiniz?
einpoklum

1
@einpoklum: C ++ 'da, etki alanlarında hala oldukça “normal” olan “nadir ve kafa karıştırıcı” sayılabilecek birçok şey vardır (derin şablon meta programlamasına bakın). Bence bu durumda "normal" tamamen geçerli; hem tarihsel hem de bugün C ++ programlama deneyiminin "normları" dahilinde olan bir şey değildir.
Nicol Bolas

@NicolBolas: Ama bu anlamda, döngüsel akıl yürütme: OP nadir bir uygulamayı benimsememiz gerekip gerekmediğini merak ediyor. Nadir uygulamalar neredeyse tanım gereği "norm" değildir. Ama nm.
einpoklum

53

Küresel lambdalardan kaçınmak isteyebileceğiniz birkaç nedeni, normal işlevlerin yerine geçmesi olarak düşünebilirim:

  • düzenli fonksiyonlar aşırı yüklenebilir; lambdas yapamaz (ancak bunu simüle etmek için teknikler vardır)
  • Fonksiyona benzer olmalarına rağmen, böyle yakalamayan bir lambda bile hafıza kaplar (genellikle yakalama için 1 bayt).
    • yorumlarda belirtildiği gibi, modern derleyiciler bu depolamayı as-if kuralı altında optimize edecek

"Neden durum bilgisi olan functorların (sınıfların) yerine lambdas kullanmamalıyım?"

  • sınıflar lambdalardan daha az kısıtlamaya sahiptir ve bu nedenle ilk ulaştığınız şey olmalıdır
    • (genel / özel veriler, aşırı yükleme, yardımcı yöntemler vb.)
  • Eğer lambda devlete sahipse, o zaman küresel hale geldiğinde akıl yürütmek daha zordur.
    • Mümkün olan en dar kapsamda bir sınıf örneği oluşturmayı tercih etmeliyiz
  • yakalamayan bir lambda'yı bir işlev işaretçisine dönüştürmek zaten zordur ve yakalamadaki herhangi bir şeyi belirten bir lambda için imkansızdır.
    • sınıflar bize işlev işaretçileri oluşturmanın basit bir yolunu sunar ve aynı zamanda birçok programcının daha rahat
  • Herhangi bir yakalamaya sahip lambdas varsayılan olarak oluşturulamaz (C ++ 20'de. Daha önce hiçbir durumda varsayılan bir yapıcı yoktu)

Ayrıca lambda'yı (yakalamayan bir tane bile) örtük "bu" işaretçisi de vardır, bu da lambda'yı çağırırken vs işlevini çağırırken fazladan bir parametreyle sonuçlanır.
1201ProgramAlarm

@ 1201ProgramAlarm: Bu iyi bir nokta; bir lambda için bir işlev işaretçisi almak çok daha zor olabilir (yakalamayan lambdalar normal işlev işaretçilerine çürümesine rağmen)
AndyG 15:19

3
Kapak tarafında, doğrudan çağırmak yerine bir yere geçirilirse, bir işlev yerine bir lambdaya sahip olmak satır içi yapmayı teşvik eder. Doğal olarak, bir işlev işaretçisi bunun std::integral_constantiçin bir paketi olabilir ...
Deduplicator

@Deduplicator: " bir fonksiyon yerine lambda'ya sahip olmak satır içi desteği teşvik eder " Sadece açık olmak gerekirse, bu sadece "bir yerde" işlev işaretçisi yerine isteğe bağlı bir çağrılabilir tür olarak adlandırılan işlevi alan bir şablonsa geçerlidir.
Nicol Bolas

2
@AndyG as-if kuralını unutuyorsunuz: Lambda'nın büyüklüğünü istemeyen (çünkü… neden ?!) veya ona bir işaretçi oluşturmayan, gerek duymayan (ve genellikle istemeyen) programlar bunun için yer ayırın.
Konrad Rudolph


8

"Normal" küresel işlevler yerine her yerde lambdaları kullanmamak için herhangi bir neden var mı?

Belirli bir karmaşıklık düzeyindeki bir sorun, en azından aynı karmaşıklığa sahip bir çözüm gerektirir. Ancak aynı sorun için daha az karmaşık bir çözüm varsa, daha karmaşık olanı kullanmanın bir gerekçesi yoktur. Neden ihtiyacınız olmayan karmaşıklığı tanıtın?

Bir lambda ve bir işlev arasında, bir işlev basitçe ikisinin daha az karmaşık bir varlık türüdür. Bir lambda kullanmamanızı haklı çıkarmak zorunda değilsiniz. Birini kullanarak haklı çıkarmalısınız. Bir lambda ifadesi, olağan özel üye işlevleri, bir işlev çağrısı operatörü ve bu durumda işlev işaretçisine bir örtülü dönüştürme operatörü olan ve bu tür bir nesne oluşturan isimsiz bir sınıf türü olan bir kapatma türü sunar. Bir global değişkeni lambda ifadesinden kopyala başlatmak, bir işlevi tanımlamaktan çok daha fazlasını yapar . Altı dolaylı işlev içeren bir sınıf türü tanımlar, iki işleç işlevi daha tanımlar ve bir nesne oluşturur. Derleyici çok daha fazlasını yapmak zorunda. Eğer lambda özelliklerinden herhangi birine ihtiyacınız yoksa lambda kullanmayın…


6

Lambdalar anonim işlevlerdir .

Adlandırılmış bir lambda kullanıyorsanız, temel olarak adsız bir işlev kullandığınız anlamına gelir. Bu oksimorondan kaçınmak için bir işlev de kullanabilirsiniz.


1
Bu terminolojiden bir argüman gibi görünüyor, yani. kafa karıştırıcı adlandırma ve anlam. Adlandırılmış bir lambda'nın artık anonim olmadığını tanımlayarak kolayca yalanlanabilir (duh). Buradaki sorun lambda'nın nasıl kullanıldığı değil, “anonim işlev” in yanlış adlandırma olması ve lambda bir isme bağlandığında artık doğru değil.
Konrad Rudolph

@KonradRudolph: Sadece terminoloji değil, bu yüzden ilk etapta yaratıldılar. En azından tarihsel olarak, lambdalar anonim işlevlerdir ve bu nedenle, özellikle işlevler beklendiğinde onları adlandırmak kafa karıştırıcıdır.
Eric Duminil

1
Hayır. Lambdalar rutin olarak adlandırılır (sadece yerel olarak), bu konuda kafa karıştırıcı bir şey yoktur.
Konrad Rudolph

1
Anonim işlevlere katılmıyorum , daha çok bir işlev oluşturuyor, ancak yine de, bunları yalnızca değer referansı olarak kullanmaya zorlamak yerine (adlandırılmış) örneğe sahip olma sorununu görmüyorum. (noktanızın yerel kapsamda da geçerli olması gerektiği için).
Jarod42

5

lambda olarak bırakmamanın bir nedeni varsa? "Normal" küresel işlevler yerine her yerde lambdaları kullanmamak için herhangi bir neden var mı?

Eskiden küresel işlev yerine işlevleri kullanırdık, bu yüzden tutarlılığı ve en az şaşkınlık ilkesini bozar .

Temel farklar şunlardır:

  • işlevler aşırı yüklenebilir, ancak işlevler yüklenemez.
  • fonksiyonları, functors ile değil ADL ile bulunabilir.
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.