C ++, 'if' ifadesinde değişken bildirimi


114

Burada neler oluyor?

if(int a = Func1())
{
    // Works.
}

if((int a = Func1()))
{
    // Fails to compile.
}

if((int a = Func1())
    && (int b = Func2()))
)
{
    // Do stuff with a and b.
    // This is what I'd really like to be able to do.
}

2003 standardındaki Bölüm 6.4.3, bir seçim ifadesi koşulunda bildirilen değişkenlerin, koşul tarafından kontrol edilen altatmanların sonuna kadar nasıl genişleyen kapsama sahip olduğunu açıklar. Ama beyanın etrafına parantez koyamamakla ilgili bir şey söylediğini nerede görmüyorum, ne de koşul başına sadece bir beyan hakkında hiçbir şey söylemiyor.

Bu sınırlama, koşulda yalnızca bir beyanın gerekli olduğu durumlarda bile can sıkıcıdır. Bunu düşün.

bool a = false, b = true;

if(bool x = a || b)
{

}

Eğer 'if' -body kapsamını x'i yanlış olarak ayarlamak istersem, bu durumda bildirim için parantez gerekir (atama operatörü mantıksal OR'den daha düşük önceliğe sahip olduğundan), ancak parantez kullanılamadığından x'in dışında bildirim gerektirir vücut, bu bildirimi istenenden daha geniş bir kapsamda sızdırıyor. Açıkçası bu örnek önemsizdir, ancak daha gerçekçi bir durum, a ve b'nin test edilmesi gereken değerleri döndüren işlevler olduğu bir durum olacaktır.

Öyleyse yapmak istediğim şey standarda uymuyor mu, yoksa derleyicim toplarımı mı kırıyor (VS2008)?


6
"Döngüye girmek istersem" <- örnekleriniz var if. ifdöngü değil, koşullu.
crashmstr

2
@crashmstr: true, ancak koşulları while ile aynıdır if.
Mike Seymour

2
Bu virgül operatörüyle yapılamaz mı? Yani: if (int a = foo(), int b = bar(), a && b)? Virgül operatörü aşırı yüklenmemişse, standart ifadelerin soldan sağa değerlendirildiğini ve sonuç değerinin son ifade olduğunu söyler. forDöngülerin başlatılmasıyla çalışır , neden burada olmasın?
Archie

@Archie: Bunu denedim, işe yaramadı. Belki çalışan bir örnek verebilirsiniz?
James Johnston 2011

@JamesJohnston: Ben de denedim ve işe yaramıyor gibi görünüyor. Bu fikir az önce kafamın tepesinden geldi, nasıl ifçalıştığına göre önerildim ve yanlış varsayım gibi görünüyor.
Archie

Yanıtlar:


63

C ++ 17'den itibaren yapmaya çalıştığınız şey nihayet mümkündür :

if (int a = Func1(), b = Func2(); a && b)
{
    // Do stuff with a and b.
}

Beyanı ve gerçek koşulu ayırmak için ;yerine ' nin kullanımına dikkat ,edin.


23
Güzel! Her zaman zamanımın ilerisinde olduğumdan şüphelenmiştim.
Neutrino

106

Sanırım konuyu zaten ima ettiniz. Derleyici bu kodla ne yapmalıdır?

if (!((1 == 0) && (bool a = false))) {
    // what is "a" initialized to?

"&&" operatörü kısa devre mantıksal bir AND'dir. Bu, eğer ilk bölüm (1==0)yanlış çıkarsa, o zaman ikinci bölüm (bool a = false)değerlendirilmemelidir çünkü son cevabın yanlış olacağı zaten bilinmektedir. Eğer (bool a = false)değerlendirilmez, daha sonra bu kullanımlara ilişkin kodla ne yapacağını a? Değişkeni başlatıp tanımsız bırakmaz mıyız? Varsayılana sıfırlar mıyız? Ya veri türü bir sınıfsa ve bunu yapmanın istenmeyen yan etkileri varsa? Ne yerine eğer boolbir sınıf kullanmış ve hiçbir varsayılan kurucu vardı böyle bir kullanıcı o olmalıdır parametreleri sağlamak - o zaman ne yapacağız?

İşte başka bir örnek:

class Test {
public:
    // note that no default constructor is provided and user MUST
    // provide some value for parameter "p"
    Test(int p);
}

if (!((1 == 0) && (Test a = Test(5)))) {
    // now what do we do?!  what is "a" set to?

Bulduğunuz sınırlama tamamen mantıklı görünüyor - bu tür belirsizliklerin olmasını engelliyor.


1
İyi bir nokta. OP veya diğerlerinin aşina olmaması durumunda, kısa devreden açıkça bahsetmek isteyebilirsiniz.
Chris Cooper 2011

7
Bunu düşünmemiştim. Örnekte kısa devre sağlamış olsanız da, koşullu ifade kapsamının girilmesini engellemiş olsanız da, bu durumda ifadenin değişkeni işlenmediğini bildiren kısmı bir sorun değildir, çünkü kapsamı koşullu ifadenin kapsamı ile sınırlıdır. Hangi durumda, derleyicinin yalnızca bir değişken bildiren ifadenin bir bölümü işlenmediğinde girilen koşullu ifade kapsamının bir olasılığının olduğu durumlarda bir hata oluşturması daha iyi olmaz mıydı? Verdiğim örneklerde durum böyle değildi.
Neutrino

@Neutrino İlk bakışta fikriniz biraz SAT problemine benziyor, ki bu da en azından genel durumda çözmesi o kadar kolay değil.
Christian Rau 2011

5
If koşulunda çok değişkenli bildirimlere sahip olmanın tüm sorunları hakkında açıkladığınız şey ve bunları yalnızca sınırlı bir şekilde kullanabilmeniz, bu tür bir bildirimin neden ilk etapta ortaya çıktığını merak etmeme neden oluyor. Bir kod örneğinde görmeden önce böyle bir sözdizimine hiç ihtiyaç duymamıştım . Bu sözdizimini oldukça beceriksiz buluyorum ve değişkeni if ​​bloğundan önce bildirmenin çok daha okunaklı olduğunu düşünüyorum. Bu değişkenin kapsamını gerçekten kısıtlamanız gerekiyorsa, if bloğunun etrafına fazladan bir blok koyabilirsiniz. Bu sözdizimini hiç kullanmadım.
Giorgio

2
Kişisel olarak, fazladan iç içe geçmiş kapsama ayraçları gibi çirkin önlemlere başvurmak zorunda kalmadan, kullandığınız değişkenlerin kapsamını, onları kullanması gereken ifade bloğunun kapsamıyla tam olarak sınırlayabilmenin oldukça zarif olacağını düşünüyorum.
Neutrino

96

Bir ifveya whileifadesindeki koşul, bir ifade veya tek bir değişken bildirimi (başlatmalı) olabilir.

İkinci ve üçüncü örnekleriniz ne geçerli ifadeler ne de geçerli bildirimlerdir, çünkü bir bildirim bir ifadenin parçası olamaz. Üçüncü örneğiniz gibi kod yazabilmek faydalı olsa da, dil sözdiziminde önemli bir değişiklik gerektirecektir.

Beyannamenin etrafına parantez koyamamakla ilgili bir şey söylediğini nerede görmüyorum, ne de koşul başına sadece bir beyan hakkında bir şey söylemiyor.

6.4 / 1'deki sözdizimi belirtimi, koşul için aşağıdakileri verir:

condition:
    expression
    type-specifier-seq declarator = assignment-expression

parantez veya diğer süslemeler olmadan tek bir bildirim belirtmek.


3
Bunun herhangi bir nedeni veya arka planı var mı?
Tomáš Zato - Monica'yı eski durumuna getir

23

Değişkenleri daha dar bir kapsamda tutmak istiyorsanız, her zaman ek { }

//just use { and }
{
    bool a = false, b = true;

    if(bool x = a || b)
    {
        //...
    }
}//a and b are out of scope

5
+1. Ek olarak, x'in bildirimini çevreleyen bloğa taşırdım: neden a ve b wrt özel statüsüne sahip olsun?
Giorgio

1
Açıktır, ancak ikna edici değildir: Aynı şey sıradan döngü değişkenleri için de söylenebilir. (Kabul edildi, sınırlı değişken kapsamına duyulan ihtiyaç döngülerde çok daha yaygındır.)
Peter - Monica'yı eski haline

18

Son bölüm zaten çalışıyor, sadece biraz farklı yazmanız gerekiyor:

if (int a = Func1())
{
   if (int b = Func2())
   {
        // do stuff with a and b
   }
}

2

İşte döngü kullanan çirkin bir geçici çözüm (her iki değişken de tamsayı ise):

#include <iostream>

int func1()
{
    return 4;
}

int func2()
{
    return 23;
}

int main()
{
    for (int a = func1(), b = func2(), i = 0;
        i == 0 && a && b; i++)
    {
        std::cout << "a = " << a << std::endl;
        std::cout << "b = " << b << std::endl;
    }

    return 0;
}

Ancak bu, diğer programcıların kafasını karıştıracaktır ve oldukça kötü bir koddur, bu yüzden önerilmez.

Basit bir kapatma {}bloğunun (zaten önerildiği gibi) okunması çok daha kolaydır:

{
    int a = func1();
    int b = func2();

    if (a && b)
    {
        std::cout << "a = " << a << std::endl;
        std::cout << "b = " << b << std::endl;
    }
}

1

Unutulmaması gereken bir nokta, daha büyük if bloğunun içindeki ifadelerin

if (!((1 == 0) && (bool a = false)))

soldan sağa bir şekilde değerlendirilmesi garanti edilmez. Gün içinde yaşadığım oldukça ince bir hata, derleyicinin aslında soldan sağa yerine sağdan sola test ediyor olması gerçeğiyle ilgiliydi.


5
Ancak günümüzde C99, && ve || soldan sağa değerlendirilir.
b0fh

2
Kısa devre mantığı nedeniyle argümanların sağdan sola değerlendirilmesinin asla mümkün olmadığını düşünüyorum. Her zaman gibi tek bir ifadede işaretçi ve nokta testi gibi şeyler için kullanılmıştır if(p && p->str && *p->str) .... Sağdan sola ölümcül olurdu ve incelikli değil. Diğer operatörlerle - hatta atama! - ve işlev çağrısı argümanları haklısınız, ancak kısa devre operatörleriyle değil.
Peter - Monica'yı Eski Haline

1

Küçük bir şablon büyüsü ile, birden çok değişkeni açıklayamama sorununu bir nevi aşabilirsiniz:

#include <stdio.h>

template <class LHS, class RHS>
struct And_t {
  LHS lhs;
  RHS rhs;

  operator bool () {
    bool b_lhs(lhs);
    bool b_rhs(rhs);
    return b_lhs && b_rhs;
  }
};
template <class LHS, class RHS> 
And_t<LHS, RHS> And(const LHS& lhs, const RHS& rhs) { return {lhs, rhs}; }

template <class LHS, class RHS>
struct Or_t {
LHS lhs;
RHS rhs;

  operator bool () {
    bool b_lhs(lhs);
    bool b_rhs(rhs);
    return b_lhs || b_rhs;
  }
};
template <class LHS, class RHS> 
Or_t<LHS, RHS> Or(const LHS& lhs, const RHS& rhs) { return {lhs, rhs}; }

int main() {
  if (auto i = And(1, Or(0, 3))) {
    printf("%d %d %d\n", i.lhs, i.rhs.lhs, i.rhs.rhs);
  }
  return 0;
}

(Not, bu kısa devre değerlendirmesini kaybeder.)


Sanırım kaybeder (veya gevşer mi?)
Peter - Monica'yı eski

5
Bence OP'nin amacı kod kısalığı, açıklık ve sürdürülebilirlikti. Siz "çözüm" önerdiğiniz tam tersini yapıyor.
Dženan
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.