Gibt es in C++ ein nicht kurzgeschlossenes logisches “und”?

Lesezeit: 10 Minuten

Benutzer-Avatar
Luciano

tl;dr: Gibt es in C++ ein logisches UND ohne Kurzschluss (ähnlich wie &&)?

Ich habe 2 Funktionen, die ich aufrufen möchte, und verwende die Rückgabewerte, um den Rückgabewert einer dritten zusammengesetzten Funktion herauszufinden. Das Problem ist, dass ich immer möchte, dass beide Funktionen ausgewertet werden (da sie Protokollinformationen über den Zustand des Systems ausgeben).

IE:

bool Func1(int x, int y){
  if( x > y){
    cout << "ERROR- X > Y" << endl;
  }
}
bool Func2(int z, int q){
  if( q * 3 < z){
    cout << "ERROR- Q < Z/3" << endl;
  }
}
bool Func3(int x, int y, int z, int q){
  return ( Func1(x, y) && Func2(z, q) );
}

Natürlich sind die Bedingungen in den Funktionen nicht ganz so einfach, und ja, mir ist klar, dass ich temporäre Variablen verwenden könnte, um die Rückgaben der beiden Funktionen zu speichern und dann die „Kurzschluss“ -Logik für die temporären Variablen auszuführen, aber Ich habe mich gefragt, ob es eine “elegante” Sprachlösung gibt, um die einzeilige Rückgabe in Func3 beizubehalten und gleichzeitig die Protokollmeldungen von beiden Funktionen zu erhalten.


Zusammenfassung der Antworten:

Die “bitweisen” Operatoren | und & können verwendet werden, um den Effekt zu erzielen, aber nur, wenn der Rückgabetyp bool ist. Ich habe keine Erwähnung davon in der ANSI C++-Spezifikation gefunden. Soweit ich das beurteilen kann, funktioniert dies, weil das “Bool” in ein Int (true = 1, False = 0) konvertiert wird und dann der bitweise Operator verwendet wird, dann wird es wieder in ein Bool konvertiert.

Die Operatoren“+” und “*” kann auch verwendet werden. Dies wird in der ANSI C++ Spezifikation nicht erwähnt, funktioniert aber wahrscheinlich aus dem gleichen Grund wie oben. “+” Geben Sie “oder” an, weil wahr in 1 umgewandelt wird und dann alles andere als 0 wieder in wahr umgewandelt wird. “*” funktioniert für “und”, weil 1 (wahr) * 0 (falsch) == 0 (falsch) und 1 (wahr) * 1 (wahr) == 1 (wahr)

Beide scheinen sich auf eine implizite Typkonvertierung in Integer und dann zurück in Bool zu verlassen. Beides wird wahrscheinlich jeden durcheinander bringen, der versucht, den Code zu warten.

Andere Antworten reduzieren sich auf “Nur Provisorien verwenden” oder “Eigene implementieren”, was nicht die Frage war. Das Ziel war zu sehen, ob es dafür bereits einen im C++-Standard implementierten Operator gibt.

  • Ich würde nur die Provisorien verwenden. Es ist mindestens so klar wie das Aufrufen der Funktionen innerhalb von if() und Sie müssen nicht nach obskuren Wegen suchen, um Ihre Methode zum Laufen zu bringen.

    – Ron Warholic

    18. November 2009 um 19:56 Uhr

  • „elegant: mühelose Schönheit und Einfachheit in Bewegung oder Ausführung zeigen“ Wenn eine temporäre Variable mühelos ist (sowohl zu schreiben als auch zu verstehen) und einfach Ihr Ziel erreicht, ist sie dann elegant?

    Roger Pate

    18. November 2009 um 20:25 Uhr

  • Ich denke, Sie können einfach temporäre flüchtige Variablen verwenden wie in: volatile uchar retVal1 = Func1(); volatile uchar retVal2 = Func2(); if(retVal1 && retVal2) { // Do my stuff } Dadurch wird sichergestellt, dass das Programm beide Variablen auswertet (auch bekannt als beide Funktionen aufruft) und Sie diese daher in Ihrer Bedingung verwenden können.

    – Davidanderle

    31. Oktober 2018 um 15:00 Uhr

  • @davidanderle, volatile wird nicht benötigt. Func1() und Func2() werden trotzdem aufgerufen.

    – Alex Che

    4. Dezember 2020 um 15:58 Uhr

Das & Operator führt eine logische “und”-Operation für aus bool Operanden und ist nicht kurzgeschlossen.

Es ist kein Sequenzpunkt. Auf die Reihenfolge der Auswertung der Operanden können Sie sich nicht verlassen. Es ist jedoch sichergestellt, dass beide Operanden ausgewertet werden.

Ich tue nicht empfehlen, dies zu tun. Die Verwendung temporärer Variablen ist eine bessere Lösung. Opfern Sie die Lesbarkeit nicht für “cleveren Code”.

  • Ich bin mir ziemlich sicher, dass dies der dokumentierte Standardweg ist, um eine boolesche Operation ohne Kurzschluss durchzuführen – verwenden Sie einfach den bitweisen Operator. Dies gilt auch für ODER (ein einzelnes |-Symbol).

    – Matt G.

    18. November 2009 um 19:56 Uhr

  • Sie sollten jeden Code, der dies tut, besser mit einem großen Kommentar versehen – jeder C-Entwickler mit mehr als einem Jahr Erfahrung würde es sehen expr1 & expr2 und “reparieren”. Die Verwendung temporärer Variablen ist eine viel bessere Lösung.

    – Graem Perrow

    18. November 2009 um 19:56 Uhr

  • Da das OP hier pedantisch war, bat es natürlich um einen Nicht-Kurzschluss logisch UND, kein bitweises UND.

    – Ed S.

    18. November 2009 um 19:59 Uhr

  • @Matt G: Das funktioniert für boolesche Werte, aber nicht im Allgemeinen. 1 && 2 Erträge truewährend 1 & 2 Erträge 0.

    – David Thornley

    18. November 2009 um 20:02 Uhr

  • Danke für die Antwort. Ich werde die temporären Variablen für den eigentlichen Code verwenden, da ich mich nicht gerne auf die “Doppelbedeutung” von “&” verlasse. Es war eher eine Frage “das scheint mehr Zeilen zu sein, als es sein muss … vielleicht gibt es eine prägnante und verständliche Sprachfunktion, die ich nicht kenne” und Google hat mich im Stich gelassen.

    – Luciano

    18. November 2009 um 20:04 Uhr

Benutzer-Avatar
Ed S.

und ja, mir ist klar, dass ich temporäre Variablen verwenden könnte, um die Rückgaben der beiden Funktionen zu speichern und dann die “Kurzschluss” -Logik für die temporären Variablen auszuführen, aber ich habe mich gefragt, ob es eine “elegante” Sprachlösung gibt, um das beizubehalten Einzeilige Rückgabe in Func3, während weiterhin die Protokollmeldungen von beiden Funktionen abgerufen werden.

Das wäre die “elegante” Lösung :). Unter Berufung auf die Nebenwirkungen der Bewertungsreihenfolge wäre alles andere als elegant, fehleranfällig und für den nächsten Entwickler, der in Ihr Projekt eindringt, schwer verständlich. Unter Berufung auf die Nebenwirkungen steht natürlich im Gegensatz zu etwas wie dem folgenden Ausschnitt, der ein völlig logischer und gültiger Anwendungsfall dafür ist, sich allein auf die Auswertungsreihenfolge zu verlassen:

if ( obj != NULL && obj->foo == blah ) { /* do stuff */ }

  • Ich würde auch empfehlen, Kommentare zu setzen, dass “diese beiden Funktionen ausgeführt werden müssen” oder so, weil sonst einer dieser optimistischen Programmierer kommen und sagen wird “Oh, diese temporären Variablen werden nicht benötigt *inline” … so oder so (& oder temp vars ) erfordern Kommentare, um ihre Verwendung zu verdeutlichen

    – Earlz

    18. November 2009 um 20:00 Uhr

  • Sich auf Nebenwirkungen zu verlassen, kann natürlich ein weiteres Zeichen dafür sein, dass hier etwas nicht stimmt.

    – Ed S.

    18. November 2009 um 20:02 Uhr

  • Zu “Sich auf die Auswertungsreihenfolge des UND-Operators zu verlassen, wäre alles andere als elegant, fehleranfällig und für den nächsten Entwickler, der in Ihr Projekt eindringt, schwer zu verstehen.”: Es gibt viele Fälle, in denen ich vorschlagen würde, sich auf die Auswertung von && zu verlassen ordnen statt verschachteln if Aussagen unnötig. Das kanonische Beispiel ist if (pSomething != NULL && pSomething->someField) ...

    – mmx

    18. November 2009 um 20:06 Uhr

  • @Mehrdad: Ja, ich mache dasselbe, aber diese Situation ist nicht wirklich dieselbe. Sie sagen: “Wenn dies nicht wahr ist, werten Sie den zweiten Teil nicht aus”, sagt das OP: “Ich möchte, dass all dies ausgewertet wird, da der nächste Funktionsaufruf von einem Nebeneffekt abhängt, obwohl das UND keine Ausdrücke auswerten muss n+1…2”. Vielleicht könnte ich es anders formulieren, um diese spezifische Verwendung anzusprechen.

    – Ed S.

    18. November 2009 um 20:26 Uhr

  • Vielleicht war sogar dieser letzte Kommentar schlecht formuliert :). Ich habe nur versucht, die Tatsache auszudrücken, dass Ihr Beispiel eine sehr saubere und klare Verwendung des Operators && ist. “Tu dies nur, wenn dies wahr ist”. Das macht Sinn. Das Beispiel im Beitrag hat diese Semantik nicht.

    – Ed S.

    18. November 2009 um 20:53 Uhr

Ja, dafür gibt es eingebaute Operatoren.
+ macht ein nicht kurzschließendes ODER und * macht ein UND.

#include <iostream>
using namespace std;

void print(bool b)
{
    cout << boolalpha << b << endl;
}

int main() 
{
    print(true + false);
    print(true * false);
}

Ausgabe:

Stimmt

FALSCH

  • Ich weiß nicht, ob ich das im Produktionscode ohne großen Kommentar machen würde, aber es ist trotzdem cool.

    – geschickt_code

    18. November 2009 um 21:43 Uhr

  • Sehr cool! Deshalb habe ich die Frage gestellt: nicht unbedingt, um es im Produktionscode zu verwenden, sondern um etwas zu lernen.

    – Luciano

    18. November 2009 um 22:27 Uhr

  • es ist nicht “cool”, es ist “Boole”. Boolesche Algebra… seufzen der ideologische Homomorphismus zwischen +/*-Operationen und UND/ODER bereits seit ungefähr 100 Jahren bekannt war?

    Benutzer719662

    20. Januar 2014 um 11:54 Uhr

  • @vaxquis vielleicht, aber viele Sprachen behandeln boolesche und ganze Zahlen gleich und erfordern nur diese Anweisungen des Formulars if (x) ... erweitert werden if (x != 0) .... Beispielsweise in Python 2.x, print(True + True) gibt “2” aus.

    – Ponkadoodle

    22. Januar 2015 um 3:19 Uhr

  • 1 || -1 wird als wahr bewertet, 1 + -1 wird als falsch bewertet, oder übersehe ich etwas? Wie wäre es mit !!a & !!b oder, für ein oder, !!a | !!b ?

    – Jacques de Hooge

    3. Februar 2020 um 14:38 Uhr


Benutzer-Avatar
Sanjaya R

Sie können trivialerweise Ihre eigenen schreiben.

bool LongCircuitAnd( bool term1, bool term2 ) { return term1 && term2; }

bool Func3(int x, int y, int z, int q){
  return LongCircuitAnd( Func1(x,y), Func2(z,q) ); 

Und wenn Sie sehr schick sein wollen, können Sie es sogar inline machen !!!

Okay, Okay, wenn Sie den schrecklichen Mehraufwand für den Aufruf einer Funktion wirklich wirklich vermeiden wollen.

bool Func3(int x, int y, int z, int q){
  return ((int)Func1(x,y)) * ((int)Func2(z,q)); 

Aber elegant finde ich das nicht. Es ist denkbar, dass ein allzu intelligenter Compiler dies kurzschließen könnte …

Wenn Sie die temporären Variablen verwenden möchten, aber die Rückkehr zu einer einzelnen Anweisung beibehalten möchten, können Sie den Kommaoperator verwenden:

return (b1=Func1()), (b2=Func2()), (b1&&b2);

Der Kommaoperator erzwingt einen Sequenzpunkt, sodass jeder seinen linken Operanden auswertet, das Ergebnis verwirft und dann seinen rechten Operanden auswertet.

Eine andere Möglichkeit, von der ich eher abraten würde, wäre, dass die beiden Funktionen einen Typ zurückgeben, der den ‘&&’-Operator überlädt. Da ein überladener Operator eine Funktion aufruft, wird sie immer ausgewertet beide Operanden, selbst in Fällen (wie &&), in denen der eingebaute Operator dies nicht tut – normalerweise ist das ein Problem, aber in diesem Fall ist es genau das, was Sie wollen:

class mybool { 
    bool value;
public:
    bool operator&&(mybool const &other) const { 
        return value && other.value;
    }
};

mybool Func1(int, int);
mybool Func2(int, int);

bool Func3(int x, int y, int z, int q) { 
    return Func1(x, y) && Func2(z,q);
}

Obwohl das funktioniert, scheint es mir ein bisschen zu “clever” zu sein – etwas, das für die meisten Leser überhaupt nicht offensichtlich wäre. Ein anderer Name für mybool könnte helfen, aber spontan fällt mir keine ein, die die Absicht sehr gut widerspiegelt, ohne so ausführlich zu werden, dass es ein Nettoverlust wäre.

  • Ich mag das. Es hält die Auswertung in Ordnung.

    – David Thornley

    18. November 2009 um 22:54 Uhr

Benutzer-Avatar
Rechnung

Ja. Die überladenen Versionen von operator&& und operator|| nicht kurzschließen — sie werten beide Operanden aus, auch wenn der linke Operand das Ergebnis “bestimmt”… (Quelle)

Davon abgesehen, nicht überladen operator&& oder operator||. Seien Sie nett zu Ihren Wartungsprogrammierern, die sich das ansehen werden && oder || und nehmen an, dass sie einen Kurzschluss machen.

  • Ich mag das. Es hält die Auswertung in Ordnung.

    – David Thornley

    18. November 2009 um 22:54 Uhr

Eine nahezu universelle, aber oft nicht dokumentierte Nicht-Standard-Operator wurde eingeführt genau zu diesem Zweck, nebenbei von GCC entwickelt x ?: y (x wenn nicht Null, sonst y), und das jetzt leider entfernt >? und <? Min/Max-Operatoren und ihre zusammengesetzten Zuweisungsformen (vgl http://gcc.gnu.org/onlinedocs/gcc/Deprecated-Features.html). Leider mit & und && bereits in Gebrauch, scheinen sie den Boden des Fasses abgekratzt zu haben, um eine geeignete Zeichenfolge zu finden, aber das ist nur meine Meinung – würde historische Erklärungen dafür begrüßen, warum dies gewählt worden sein könnte.

Obwohl es derzeit nicht so bekannt ist wie viele andere Betreiber, das >! Operator (richtig, aber langweilig “Long-Circuit and” genannt, aber umgangssprachlich “größerer Knoten” von Kennern) wurde von den meisten C- und C++-Compilern (einschließlich GCC und sogar MSVC++) hinzugefügt, um diese Anforderung zu erfüllen:

bool f1() { ... }
bool f2() { ... }

...
bool f3() { return f1() >! f2(); }

Probieren Sie es aus ;-).

  • Wie eine (inzwischen gelöschte) Antwort darauf hinweist, handelt es sich nicht wirklich um einen neuen Operator (genauso wie --> ist nicht), sondern einfach eine seltsam formatierte a > !b was genauso funktioniert wie a && b außer nicht kurzgeschlossen, so lange wie weder a oder b sind negativ.

    – Kubisch

    24. Oktober 2018 um 16:01 Uhr

1018940cookie-checkGibt es in C++ ein nicht kurzgeschlossenes logisches “und”?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy