C++, deklaracja zmiennej w wyrażeniu "if"

Co tu się dzieje?

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.
}

Sekcja 6.4.3 w standardzie 2003 wyjaśnia, w jaki sposób zmienne zadeklarowane w warunku instrukcji wyboru mają zakres, który rozciąga się na koniec podstacji kontrolowanych przez warunek. Ale nie widzę, gdzie mówi coś o tym, że nie jest w stanie umieścić nawiasu wokół deklaracji, ani nie mówi nic o tylko jednej deklaracji na warunek.

To ograniczenie jest irytujące nawet w przypadkach, gdy tylko jedna deklaracja w tym stanie jest wymagane. Rozważ to.

bool a = false, b = true;

if(bool x = a || b)
{

}

Jeśli chcę wprowadzić'if' -zakres ciała z X ustawionym na false, wtedy deklaracja wymaga nawiasu (ponieważ operator przypisania ma niższy priorytet niż logiczny OR), ale ponieważ nawias nie może być użyty, wymaga deklaracji x poza ciałem, wyciekając tę deklarację do większego zakresu niż jest to pożądane. Oczywiście ten przykład jest trywialny, ale bardziej realistyczny byłby przypadek, w którym A i b są funkcjami zwracającymi wartości które trzeba przetestować

Więc to, co chcę zrobić, jest niezgodne ze standardem, czy mój kompilator po prostu pęka mi po jajach (VS2008)?

Author: Scheff, 2011-10-20

8 answers

Od C++17 to co próbowałeś zrobić jest w końcu możliwe :

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

Zwróć uwagę na użycie ; zamiast , do oddzielenia deklaracji od stanu faktycznego.

 81
Author: fwyzard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-25 09:51:17

Myślę, że już wspomniałeś o tym problemie. Co powinien zrobić kompilator z tym kodem?

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

Operator "&&" jest zwarciem logicznym i. Oznacza to, że jeśli pierwsza część (1==0) okaże się fałszywa, to druga część (bool a = false) nie powinna być oceniana, ponieważ wiadomo już, że ostateczna odpowiedź będzie fałszywa. Jeśli (bool a = false) nie jest oceniany, to co później zrobić z kodem, który używa a? Czy po prostu nie zainicjalizujemy zmiennej i nie pozostawiamy jej niezdefiniowanej? Would inicjalizujemy go na domyślne? Co zrobić, jeśli typ danych był klasą i robi to miało niepożądane skutki uboczne? Co jeśli zamiast bool użyłeś klasy i nie miała ona domyślnego konstruktora, tak że użytkownik musi podać parametry - co wtedy zrobimy?

Oto kolejny przykład:

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?

Wydaje się, że ograniczenie, które znalazłeś, wydaje się całkowicie rozsądne - zapobiega powstawaniu tego rodzaju niejasności.

 106
Author: James Johnston,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-20 14:51:53

Warunek w instrukcji if lub while może być albo wyrażeniem , albo pojedynczą zmiennądeklaracją (z inicjalizacją).

Twój drugi i trzeci przykład nie są ani poprawnymi wyrażeniami, ani poprawnymi deklaracjami, ponieważ deklaracja nie może stanowić części wyrażenia. Chociaż byłoby przydatne, aby móc pisać kod, jak twój trzeci przykład, wymagałoby to znaczącej zmiany składni języka.

Nie widzę, gdzie jest napisane nic o tym, że nie jest w stanie umieścić nawiasu wokół deklaracji, ani nie mówi nic o tylko jednej deklaracji na warunek.

Specyfikacja składni w 6.4 / 1 daje następujący warunek:

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

Określenie pojedynczej deklaracji, bez nawiasów i innych ozdobników.

 98
Author: Mike Seymour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-26 11:42:33

Jeśli chcesz załączyć zmienne w węższym zakresie, zawsze możesz użyć dodatkowego { }

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

    if(bool x = a || b)
    {
        //...
    }
}//a and b are out of scope
 23
Author: crashmstr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-20 14:12:27

Ostatni dział już działa, Wystarczy napisać trochę inaczej:

if (int a = Func1())
{
   if (int b = Func2())
   {
        // do stuff with a and b
   }
}
 18
Author: Bo Persson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-20 16:38:40

Oto brzydkie obejście za pomocą pętli (jeśli obie zmienne są liczbami całkowitymi):

#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;
}

Ale to myli innych programistów i jest to raczej zły kod, więc nie zaleca się.

Prosty blok {} (Jak już zalecono) jest znacznie łatwiejszy do odczytania:

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

    if (a && b)
    {
        std::cout << "a = " << a << std::endl;
        std::cout << "b = " << b << std::endl;
    }
}
 2
Author: basic6,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-06 15:52:46

Należy również zauważyć, że wyrażenia wewnątrz większego bloku if

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

Niekoniecznie muszą być oceniane w sposób od lewej do prawej. Jeden dość subtelny błąd, który miałem kiedyś, miał związek z faktem, że kompilator testował od prawej do lewej zamiast od lewej do prawej.

 1
Author: DukeBrymin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-25 19:45:41

Z odrobiną magii szablonów można jakoś obejść problem braku możliwości deklarowania wielu zmiennych:

#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;
}

(Uwaga, powoduje to utratę oceny zwarcia.)

 1
Author: BCS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-15 23:50:28