C ++ 'da Çifte Olumsuzluk


124

Az önce oldukça büyük bir kod tabanı olan bir projeye geldim.

Çoğunlukla C ++ ile uğraşıyorum ve yazdıkları kodun çoğu boole mantığı için çift olumsuzlama kullanıyor.

 if (!!variable && (!!api.lookup("some-string"))) {
       do_some_stuff();
 }                                   

Bu adamların zeki programcılar olduklarını biliyorum, bunu kazara yapmadıkları açık.

Deneyimli bir C ++ uzmanı değilim, bunu neden yaptıklarına dair tek tahminim, değerlendirilen değerin gerçek boole temsili olduğu konusunda kesinlikle pozitif yapmak istedikleridir. Böylece, onu olumsuzlarlar, sonra onu gerçek boole değerine geri döndürmek için tekrar reddederler.

Bu doğru mu yoksa bir şey mi kaçırıyorum?



Bu konu burada tartışılmıştır .
Dima

1
Olası Is
EdChum

Yanıtlar:


121

Bool'a dönüştürmek için bir numara.


19
Bence bunu açıkça (bool) ile çevirmek daha açık olur, neden bu zor !!, çünkü daha az yazı yazıyor?
Baiyan Huang

27
Bununla birlikte, C ++ veya modern C'de veya sonucun yalnızca boole ifadesinde kullanıldığı yerde (soruda olduğu gibi) anlamsızdır. Hiçbir varken yararlıdır geri dönmüştü booldışındaki değerleri depolamak yardım kaçınmak için tipini, 1ve 0boolean değişkenlerde.
Mike Seymour

6
@lzprgmr: açık atama, MSVC'de bir "performans uyarısına" neden olur . Sorunu kullanmak !!veya !=0çözer ve ikisi arasında eski temizleyiciyi buluyorum (çünkü daha fazla miktarda tür üzerinde çalışacaktır). Ayrıca söz konusu kodda kullanmak için bir neden olmadığını da kabul ediyorum.
Yakov Galka

6
@Noldorin, okunabilirliği artırdığını düşünüyorum - ne anlama geldiğini biliyorsanız, basit, temiz ve mantıklı.
jwg

19
Geliştirir? Lanet olsun ... bana içtiğin şeyden biraz ver.
Noldorin

73

Aslında bazı bağlamlarda çok kullanışlı bir deyim. Bu makroları alın (örneğin Linux çekirdeğinden). GCC için aşağıdaki şekilde uygulanırlar:

#define likely(cond)   (__builtin_expect(!!(cond), 1))
#define unlikely(cond) (__builtin_expect(!!(cond), 0))

Bunu neden yapmak zorundalar? GCC __builtin_expect, parametrelerini kabul eder longve etmez bool, bu nedenle bir tür dönüşüm olması gerekir. condBu makroları yazarken ne olduğunu bilmedikleri için, en genel olarak !!deyimi kullanmak basittir .

Muhtemelen 0 ile karşılaştırarak aynı şeyi yapabilirler, ancak bana göre, C'nin sahip olduğu bir bool'a en yakın olanı bu olduğundan, çifte olumsuzlamayı yapmak aslında daha basittir.

Bu kod C ++ 'da da kullanılabilir ... en düşük ortak payda bir şeydir. Mümkünse, hem C hem de C ++ 'da işe yarayan şeyi yapın.


Düşündüğünüzde bunun çok mantıklı olduğunu düşünüyorum. Her cevabı okumadım ama görünüşe göre dönüştürme işlemi belirtilmemiş. Eğer 2 bit yüksek ve sadece bir bit yüksek değere sahip bir değerimiz varsa, sıfır olmayan bir değere sahip oluruz. Sıfır olmayan bir değerin olumsuzlanması bir boole dönüşümüyle sonuçlanır (sıfırsa false, aksi halde doğru). Sonra tekrar reddetmek, orijinal gerçeği temsil eden bir mantıkla sonuçlanır.
Joey Carson

SO, yorumumu güncellememe izin vermediğinden, hatama bir düzeltme ekleyeceğim. Bir integral değerin olumsuzlanması, bir boole dönüşümüne neden olur (sıfır değilse false, aksi halde doğru).
Joey Carson

51

Kodlayıcılar, işleneni bool'a dönüştürebileceğini düşünüyorlar, ancak && işlenenleri zaten dolaylı olarak bool'a dönüştürüldüğünden, tamamen gereksizdir.


14
Visual C ++, bazı durumlarda bu numara olmadan performans düşüşü sağlar.
Kirill V.Lyadvinsky

1
Koddaki gereksiz uyarılar etrafında çalışmaktansa uyarıyı devre dışı bırakmanın en iyisi olacağını düşünüyorum.
Ruslan

Belki de farkında değillerdir. Bu, farkında olmadığınız entegre veri türleriyle çalışabileceğiniz bir makro bağlamında mükemmel bir anlam ifade ediyor. Bir bit alanını temsil eden integral değeri döndürmek için aşırı yüklenmiş parantez operatörüne sahip nesneleri düşünün.
Joey Carson

12

Yazmaktan kaçınmak için bir tekniktir (değişken! = 0) - yani, hangi türden olursa olsun bir bool'a dönüştürmek.

Bunun gibi IMO Kodunun bakımı gereken sistemlerde yeri yoktur - çünkü hemen okunabilir bir kod değildir (bu nedenle ilk etapta soru).

Kod okunaklı olmalıdır - aksi takdirde gelecek için bir zaman borcu mirası bırakırsınız - çünkü gereksiz yere kıvrılan bir şeyi anlamak zaman alır.


8
Hile tanımım, herkesin ilk okumada anlayamayacağı bir şey. Çözülmesi gereken bir şey bir hile. Ayrıca korkunç çünkü! operatör aşırı yüklenmiş olabilir ...
Richard Harrison

6
@ orlandu63: basit tipleme: bool(expr)doğru olanı yapar ve herkes ilk bakışta amacı anlar. !!(expr)yanlışlıkla bool'a dönüşen bir çifte olumsuzlamadır ... bu basit değil.
Adrien Plisson

12

Evet doğru ve hayır bir şeyi kaçırmıyorsun. !!bool'a bir dönüşümdür. Daha fazla tartışma için bu soruya bakın .


9

Bir derleyici uyarısına yandan adım atar. Bunu dene:

int _tmain(int argc, _TCHAR* argv[])
{
    int foo = 5;
    bool bar = foo;
    bool baz = !!foo;
    return 0;
}

"Bar" satırı, MSVC ++ üzerinde "doğru" ya da "yanlış" (performans uyarısı) bool yapmaya zorlayan bir değer "üretir, ancak" baz "satırı, cezadan gizlice geçer.


1
En yaygın olarak bilmediği Windows API kendisi karşılaşılan booltip - her şey olarak kodlanmış 0veya 1bir in int.
Mark Ransom

4

Operatördür! aşırı?
Değilse, muhtemelen bunu bir uyarı üretmeden değişkeni bir bool'a dönüştürmek için yapıyorlar. Bu kesinlikle bir şeyler yapmanın standart bir yolu değil.


4

Legacy C geliştiriciler genellikle bu yüzden, hiçbir Boole türü vardı #define TRUE 1ve #define FALSE 0ardından Boole karşılaştırmaları için keyfi sayısal veri türleri kullanılır. Artık sahip olduğumuza göre bool, birçok derleyici, sayısal türlerin ve Boolean türlerinin bir karışımı kullanılarak belirli türdeki atamalar ve karşılaştırmalar yapıldığında uyarılar yayınlayacaktır. Bu iki kullanım, eski kodla çalışırken sonunda çakışacaktır.

Bu soruna geçici bir çözüm bulmak için, bazı geliştiriciler aşağıdaki Boole kimliğini kullanır: eğer !num_valuedöndürür ; aksi takdirde. eğer döndürür ; aksi takdirde. Tek olumsuzlama, dönüştürmek için yeterlidir ; ancak, Boole ifadesinin orijinal anlamını geri yüklemek için çifte olumsuzlama gereklidir.bool truenum_value == 0false!!num_valuebool falsenum_value == 0truenum_valuebool

Bu kalıp deyim olarak bilinir , yani dile aşina olan insanlar tarafından yaygın olarak kullanılan bir şeydir. Bu nedenle, bunu benim kadar bir anti-model olarak görmüyorum static_cast<bool>(num_value). Oyuncu kadrosu çok iyi sonuçları verebilir, ancak bazı derleyiciler daha sonra bir performans uyarısı verir, bu nedenle yine de bunu ele almanız gerekir.

Bunu ele almanın diğer yolu, şunu söylemektir (num_value != FALSE). Bunda da sorun yok, ama sonuçta !!num_valueçok daha az ayrıntılı, daha net olabilir ve ikinci kez gördüğünüzde kafa karıştırıcı değil.


2

!! boole tipine sahip olmayan orijinal C ++ ile başa çıkmak için kullanıldı (C'nin de yapmadığı gibi).


Örnek Problem:

İçeride if(condition), vb. conditionGibi bir tür için değerlendirme ihtiyacı var double, int, void*, ancak boolhenüz mevcut olmadığı gibi değil.

Bir sınıfın var olduğunu int256(256 bitlik bir tamsayı) ve tüm tamsayı dönüşümlerinin / yayınlarının aşırı yüklendiğini varsayalım.

int256 x = foo();
if (x) ...

x"Doğru" olup olmadığını veya sıfır olmadığını test etmek için, bir tam sayıya if (x)dönüştürülür xve ardından bunun intsıfır olmadığı değerlendirilir . Tipik bir aşırı yük, (int) xyalnızca x. if (x)o zaman sadece LSbitlerini test ediyordu x.

Ancak C ++ 'ın !operatörü vardır. Aşırı yüklenme !x, tipik olarak tüm bitlerini değerlendirir x. Yani tersine çevrilmemiş mantığa geri dönmek için if (!!x)kullanılır.

Ref C ++ 'nın eski sürümleri, bir "if ()" ifadesindeki koşulu değerlendirirken bir sınıfın "int" operatörünü kullanıyor muydu?


1

As Marcin söz operatör yükleme oyunda ise, iyi bir önemi olabilir. Aksi takdirde, C / C ++ 'da aşağıdakilerden birini yapıp yapmamanız dışında önemli değildir:

  • doğrudan karşılaştırma true(veya C'de TRUEmakro gibi bir şey ), ki bu neredeyse her zaman kötü bir fikirdir. Örneğin:

    if (api.lookup("some-string") == true) {...}

  • bir şeyin katı bir 0/1 değerine dönüştürülmesini istiyorsunuz. C ++ 'da a'ya bir atama boolbunu örtük olarak yapar (örtük olarak dönüştürülebilen şeyler için bool). C'de veya bool olmayan bir değişkenle uğraşıyorsanız, bu gördüğüm bir deyim, ancak ben (some_variable != 0)çeşitliliği tercih ederim .

Bence daha büyük bir mantıksal ifade bağlamında, sadece işleri karıştırıyor.


1

Eğer değişken bir nesne türünden olup, bir olabilir! işleç tanımlı, ancak bool'a dönüştürme yok (veya daha kötüsü, farklı anlambilimlere sahip int'e örtük bir dönüştürme.! operatörünü iki kez çağırmak, garip durumlarda bile çalışan bir bool'a dönüştürmeyle sonuçlanır.


0

Bu doğru ama, burada anlamsız - 'eğer' ve '&&' ifadeye '!!' olmadan aynı şekilde davranır.

Bunu C ++ 'da yapmanın nedeni, sanırım,' && 'aşırı yüklenmiş olabilir. Ama sonra, bu yüzden olabilir '!', Değil bu yüzden gerçekten sen türleri için kod bakmadan, bir bool olsun garanti variableve api.call. Belki daha fazla C ++ deneyimi olan biri açıklayabilir; Belki de bu, bir garanti değil, derinlemesine savunma amaçlı bir önlemdir.


Derleyici, yalnızca ifveya için bir işlenen olarak kullanıldığında değerleri her iki şekilde de ele alır &&, ancak kullanımı ; ile değiştirilirse !!bazı derleyicilerde yardımcı olabilir ; bit türlerine sahip bazı gömülü derleyicilerde, örneğin bir bit türüne 256 atamak sıfır verir. Bu olmadan, görünüşte güvenli olan dönüşüm ( koşulu bir değişkene kopyalamak ve sonra dallara ayırmak) güvenli olmayacaktır. if (!!(number & mask))bit triggered = !!(number & mask); if (triggered)!!if
supercat

0

Belki programcılar böyle bir şey düşünüyorlardı ...

!! myAnswer boole'dir. Bağlam olarak, boolean olmalı, ama emin olmak için bir şeyleri patlatmayı seviyorum, çünkü bir zamanlar beni ısırtan gizemli bir böcek vardı ve patlama bang, onu öldürdüm.


0

Bu, çifte patlama numarasına bir örnek olabilir , daha fazla ayrıntı için Güvenli Bool Deyimi'ne bakın. Burada makalenin ilk sayfasını özetliyorum.

C ++ 'da, sınıflar için Boolean testleri sağlamanın birkaç yolu vardır.

Açık bir yol, operator booldönüştürme operatörüdür.

// operator bool version
  class Testable {
    bool ok_;
  public:
    explicit Testable(bool b=true):ok_(b) {}

    operator bool() const { // use bool conversion operator
      return ok_;
    }
  };

Sınıfı test edebiliriz,

Testable test;
  if (test) 
    std::cout << "Yes, test is working!\n";
  else 
    std::cout << "No, test is not working!\n";

Ancak, veya opereator boolgibi anlamsız işlemlere izin verdiği için güvensiz olarak kabul edilir .test << 1;int i=test

operator!Örtülü dönüştürme veya aşırı yükleme sorunlarından kaçındığımız için kullanmak daha güvenlidir.

Uygulama önemsizdir,

bool operator!() const { // use operator!
    return !ok_;
  }

TestableNesneyi test etmenin iki deyimsel yolu

  Testable test;
  if (!!test) 
    std::cout << "Yes, test is working!\n";
  if (!test2) {
    std::cout << "No, test2 is not working!\n";

İlk versiyon if (!!test), bazılarının çift ​​patlama numarası olarak adlandırdığı şeydir .


1
C ++ 11'den beri, explicit operator booldiğer integral türlerine örtük dönüştürmeyi önlemek için kullanılabilir .
Arne Vogel
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.