webentwicklung-frage-antwort-db.com.de

C++, Variablendeklaration im 'if'-Ausdruck

Was ist denn hier los?

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

In Abschnitt 6.4.3 des Standards von 2003 wird erläutert, wie in einer Auswahlanweisungsbedingung deklarierte Variablen einen Gültigkeitsbereich haben, der bis zum Ende der von der Bedingung kontrollierten Unteranweisungen reicht. Ich sehe jedoch nicht, wo es irgendetwas darüber aussagt, dass es nicht in der Lage ist, die Deklaration in Klammern zu setzen, oder dass es nur eine Deklaration pro Bedingung gibt.

Diese Einschränkung ist ärgerlich, selbst wenn nur eine Deklaration in der Bedingung erforderlich ist. Bedenken Sie.

bool a = false, b = true;

if(bool x = a || b)
{

}

Wenn ich den Bereich "if" eingeben möchte, wenn x auf false gesetzt ist, benötigt die Deklaration Klammern (da der Zuweisungsoperator eine niedrigere Priorität als das logische ODER hat). Da Klammern jedoch nicht verwendet werden können, ist die Deklaration von x außerhalb erforderlich Wenn Sie die Deklaration in einem größeren Umfang als gewünscht durchgesickert haben, ist dieses Beispiel offensichtlich trivial, aber ein realistischerer Fall wäre der Fall, in dem a und b Funktionen sind, die Werte liefern, die getestet werden müssen 

Ist das, was ich will, nicht dem Standard entsprechen oder bricht mein Compiler gerade meine Bälle (VS2008)?

96
Neutrino

Die Bedingung in einer if- oder while-Anweisung kann entweder ein Ausdruck oder eine einzelne Variable Deklaration sein (mit Initialisierung).

Ihr zweites und drittes Beispiel sind weder gültige Ausdrücke noch gültige Deklarationen, da eine Deklaration nicht Teil eines Ausdrucks sein kann. Es wäre zwar nützlich, Code wie in Ihrem dritten Beispiel schreiben zu können, aber es wäre eine erhebliche Änderung der Sprachsyntax erforderlich.

Ich sehe weder, wo es etwas darüber aussagt, dass man die Deklaration nicht in Klammern setzen kann, noch sagt es nur eine Deklaration pro Bedingung aus.

Die Syntaxspezifikation in 6.4/1 gibt für die Bedingung Folgendes an:

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

angabe einer einzelnen Deklaration ohne Klammern oder sonstige Verzierungen.

88
Mike Seymour

Ich denke, Sie haben das Thema bereits angedeutet. Was soll der Compiler mit diesem Code machen?

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

Der Operator "&&" ist ein logisches UND für den Kurzschluss. Das heißt, wenn sich herausstellt, dass der erste Teil (1==0) falsch ist, sollte der zweite Teil (bool a = false) nicht ausgewertet werden, da bereits bekannt ist, dass die endgültige Antwort falsch ist. Wenn (bool a = false) nicht ausgewertet wird, was soll dann mit Code geschehen, der später a verwendet? Würden wir die Variable einfach nicht initialisieren und sie undefiniert lassen? Würden wir es auf den Standard setzen? Was wäre, wenn der Datentyp eine Klasse wäre und dies unerwünschte Nebenwirkungen hätte? Was wäre, wenn Sie anstelle von bool eine Klasse verwendet hätten und sie keinen Standardkonstruktor hätte, so dass der Benutzer Parameter angeben muss - Parameter angeben - was machen wir dann?

Hier ist ein anderes Beispiel:

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?

Sieht so aus, als scheint die von Ihnen gefundene Einschränkung durchaus vernünftig - sie verhindert, dass solche Mehrdeutigkeiten auftreten.

97
James Johnston

Ab C++ 17 ist das, was Sie versucht haben, endlich möglich :

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

Beachten Sie die Verwendung von ; von anstelle von ,, um die Deklaration von der tatsächlichen Bedingung zu trennen.

32
fwyzard

Wenn Sie Variablen in einem engeren Bereich einschließen möchten, können Sie immer zusätzliche { } verwenden.

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

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

Der letzte Abschnitt funktioniert bereits, Sie müssen nur etwas anders schreiben:

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

Hier ist eine hässliche Problemumgehung mithilfe einer Schleife (wenn beide Variablen ganze Zahlen sind):

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

Dies wird jedoch andere Programmierer verwirren, und es ist ziemlich schlechter Code, also nicht zu empfehlen.

Ein einfacher einschließender {}-Block (wie bereits empfohlen) ist viel einfacher zu lesen:

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

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

Zu beachten ist auch, dass sich die Ausdrücke im größeren if-Block befinden

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

sind nicht unbedingt garantiert von links nach rechts bewertet. Ein eher subtiler Fehler, den ich damals hatte, hatte damit zu tun, dass der Compiler von rechts nach links statt von links nach rechts getestet wurde.

1
DukeBrymin

Mit einer kleinen Schablonenmagie können Sie das Problem umgehen, dass Sie nicht mehrere Variablen deklarieren können:

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

(Beachten Sie, dass dadurch die Kurzschlussbewertung verloren geht.) 

0
BCS