Ist es möglich, zwei Variablen unterschiedlichen Typs in einer for-Schleife zu deklarieren?

240

Ist es möglich, zwei Variablen unterschiedlichen Typs im Initialisierungskörper einer for-Schleife in C ++ zu deklarieren?

Beispielsweise:

for(int i=0,j=0 ...

definiert zwei ganze Zahlen. Kann ich im Initialisierungskörper ein intund ein definieren char? Wie würde das gemacht werden?

Nathan Osman
quelle
3
Es ist in g ++ - 4.4 ( -std=c++0x) in Form von möglich for(auto i=0, j=0.0; ..., aber diese Möglichkeit wurde in g ++ - 4.5 entfernt, um mit den c ++ 0x-Texten übereinzustimmen.
Rafak

Antworten:

133

C ++ 17 : Ja! Sie sollten eine strukturierte Bindungsdeklaration verwenden . Die Syntax wird seit Jahren in gcc und clang unterstützt (seit gcc-7 und clang-4.0) ( Beispiel für clang live ). Dies ermöglicht es uns, ein Tupel wie folgt auszupacken:

for (auto [i, f, s] = std::tuple{1, 1.0, std::string{"ab"}}; i < N; ++i, f += 1.5) {
    // ...
}

Das Obige gibt Ihnen:

  • int i einstellen 1
  • double f einstellen 1.0
  • std::string s einstellen "ab"

Stellen Sie sicher, #include <tuple>für diese Art von Erklärung.

Sie können die genauen Typen innerhalb tuplevon angeben, indem Sie sie alle wie bei der eingeben std::string, wenn Sie einen Typ benennen möchten. Beispielsweise:

auto [vec, i32] = std::tuple{std::vector<int>{3, 4, 5}, std::int32_t{12}}

Eine spezielle Anwendung davon ist das Durchlaufen einer Karte, um den Schlüssel und den Wert zu erhalten.

std::unordered_map<K, V> m = { /*...*/ };
for (auto& [key, value] : m) {
   // ...
}

Sehen Sie ein Live - Beispiel hier


C ++ 14 : Sie können dasselbe wie C ++ 11 (unten) tun, indem Sie typbasiert hinzufügen std::get. Anstelle des std::get<0>(t)folgenden Beispiels können Sie also haben std::get<int>(t).


C ++ 11 : std::make_pairErmöglicht dies sowie std::make_tuplefür mehr als zwei Objekte.

for (auto p = std::make_pair(5, std::string("Hello World")); p.first < 10; ++p.first) {
    std::cout << p.second << std::endl;
}

std::make_pairgibt die beiden Argumente in a zurück std::pair. Auf die Elemente kann mit .firstund zugegriffen werden .second.

Für mehr als zwei Objekte müssen Sie a verwenden std::tuple

for (auto t = std::make_tuple(0, std::string("Hello world"), std::vector<int>{});
        std::get<0>(t) < 10;
        ++std::get<0>(t)) {
    std::cout << std::get<1>(t) << std::endl; // cout Hello world
    std::get<2>(t).push_back(std::get<0>(t)); // add counter value to the vector
}

std::make_tupleist eine variable Vorlage, die ein Tupel mit einer beliebigen Anzahl von Argumenten erstellt (natürlich mit einigen technischen Einschränkungen). Auf die Elemente kann per Index mit zugegriffen werdenstd::get<INDEX>(tuple_object)

Innerhalb der for - Schleife Körper können Sie einfach alias die Objekte, obwohl Sie noch brauchen , um den Einsatz .firstoder std::getfür die for - Schleife Zustand und Aktualisierungsausdruck

for (auto t = std::make_tuple(0, std::string("Hello world"), std::vector<int>{});
        std::get<0>(t) < 10;
        ++std::get<0>(t)) {
    auto& i = std::get<0>(t);
    auto& s = std::get<1>(t);
    auto& v = std::get<2>(t);
    std::cout << s << std::endl; // cout Hello world
    v.push_back(i); // add counter value to the vector
}

C ++ 98 und C ++ 03 Sie können die Typen von a explizit benennen std::pair. Es gibt jedoch keine Standardmethode, um dies auf mehr als zwei Typen zu verallgemeinern:

for (std::pair<int, std::string> p(5, "Hello World"); p.first < 10; ++p.first) {
    std::cout << p.second << std::endl;
}
Ryan Haining
quelle
5
Wenn Sie C ++ 17 ausführen, können Sie das sogar löschen make_und schreiben std::pair(1, 1.0).
Marc Glisse
Das haarige Tupel / Paar-Geschäft im C ++ 14-Stil - alles gut (wahrscheinlich positiv bewertet), sieht aber bizarr aus :)
mlvljr
3
Kurzum: Ja, es ist möglich, aber es wird nicht schön.
Einige Programmierer Typ
Ja, nicht hübsch, aber es ist doof! Absolut genossen das Tupel. :) Aber wirklich, es ist eine sehr unintuitive syntaktische Qualität von for-Schleifen in C ++ und hat mir mehr als eine halbe Stunde lang Kopfschmerzen bereitet, um endlich zu erkennen, was gegoogelt werden musste ...
aderchox
@aderchox Wenn Sie Ihr Missverständnis klären können, kann ich die Antwort aktualisieren
Ryan Haining
276

Nein - aber technisch gesehen gibt es eine Umgehung (nicht, dass ich sie tatsächlich verwenden würde, wenn ich nicht dazu gezwungen würde):

for(struct { int a; char b; } s = { 0, 'a' } ; s.a < 5 ; ++s.a) 
{
    std::cout << s.a << " " << s.b << std::endl;
}
Georg Fritzsche
quelle
3
Dies wird nicht auf VS 2008 kompiliert, sondern auf Comeau online ;-)
JRL
7
@JRL: Oh, VS2005 auch nicht. Noch eine weitere Nichteinhaltungsfunktion in VC ++, denke ich.
Georg Fritzsche
3
Ich habe das Äquivalent in Perl gemacht. Ich habe jedoch nicht versucht, so etwas durch eine Codeüberprüfung in C ++ zu schleichen.
John
21
Mit C ++ 11 I können Sie dieses Beispiel mit Standardwerten verkürzenstruct { int a=0; char b='a'; } s;
Ryan Haining
1
Diese Antwort erfüllt die Anforderungen der Antwort, aber von einem lesbaren POV bevorzuge ich @MK. Antwort. Die Lösung von MK adressiert sogar das Scoping, indem die geschweiften Klammern hinzugefügt werden.
Trevor Boyd Smith
221

Nicht möglich, aber Sie können:

float f;
int i;
for (i = 0,f = 0.0; i < 5; i++)
{
  //...
}

Oder beschränken Sie den Umfang fund die iVerwendung zusätzlicher Klammern explizit :

{
    float f; 
    int i;
    for (i = 0,f = 0.0; i < 5; i++)
    {
       //...
    }
}
MK.
quelle
Ich weiß, dass dies eine sehr alte Frage ist, aber können Sie erklären, warum einige dies mit den zusätzlichen Klammern tun würden, wie in Ihrem zweiten Beispiel?
Ford
13
@fizzisist, um den Umfang von f und i explizit auf nur Teile des Codes zu beschränken, in denen sie verwendet werden.
MK.
1
@MK. Danke, das habe ich vermutet. Ich habe Ihre Antwort bearbeitet, um das zu erklären.
Ford
Nur eine Frage: Warum so? : O
Rohan-Patel
Da es wie 'int a = 0, b = 4' funktioniert, nehme ich an. Abgesehen davon ist das Scoping von f und i wahrscheinlich nur nützlich, um die Wiederverwendung dieser Namen zu verhindern (was ein fairer Grund ist), aber der generierte Code ist normalerweise auf einem modernen Compiler (in diesem Fall) der gleiche.
Asu
14

Sie können bei der Initialisierung nicht mehrere Typen deklarieren, aber Sie können mehreren Typen EG zuweisen

{
   int i;
   char x;
   for(i = 0, x = 'p'; ...){
      ...
   }
}

Erklären Sie sie einfach in ihrem eigenen Umfang.

zmbush
quelle
3

Ich denke, der beste Ansatz ist Xians Antwort .

aber...


# Verschachtelt für Schleife

Dieser Ansatz ist schmutzig, kann aber in jeder Version gelöst werden.

Daher verwende ich es oft in Makrofunktionen.

for(int _int=0, /* make local variable */ \
    loopOnce=true; loopOnce==true; loopOnce=false)

    for(char _char=0; _char<3; _char++)
    {
        // do anything with
        // _int, _char
    }

Zusätzliche 1.

Es kann auch verwendet werden, um declare local variablesund initialize global variables.

float globalFloat;

for(int localInt=0, /* decalre local variable */ \
    _=1;_;_=0)

    for(globalFloat=2.f; localInt<3; localInt++) /* initialize global variable */
    {
        // do.
    }

Zusätzliche 2.

Gutes Beispiel: mit Makrofunktion.

(Wenn der beste Ansatz nicht verwendet werden kann, weil es sich um ein For-Loop-Makro handelt)

#define for_two_decl(_decl_1, _decl_2, cond, incr) \
for(_decl_1, _=1;_;_=0)\
    for(_decl_2; (cond); (incr))


    for_two_decl(int i=0, char c=0, i<3, i++)
    {
        // your body with
        // i, c
    }

# If-Anweisungstrick

if (A* a=nullptr);
else
    for(...) // a is visible

Wenn Sie auf 0oder initialisieren möchten nullptr, können Sie diesen Trick verwenden.

aber ich empfehle dies nicht wegen der harten Lektüre.

und es scheint wie ein Fehler.

mgcation
quelle
Es überrascht mich immer wieder, wie anders manche Menschen von anderen denken. An solche Kuriositäten hätte ich nie gedacht. Interessante Ideen.
Dr. Person Person II
1

Weitere Informationen zum Verschachteln mehrerer for-Schleifen finden Sie unter " Gibt es eine Möglichkeit, Variablen zweier Typen in der for-Schleife zu definieren? ". Der Vorteil des anderen Weges gegenüber Georgs "Strukturtrick" besteht darin, dass Sie (1) eine Mischung aus statischen und nicht statischen lokalen Variablen und (2) nicht kopierbare Variablen haben können. Der Nachteil ist, dass es weitaus weniger lesbar und möglicherweise weniger effizient ist.

tgoodhart
quelle
-2

Makro definieren:

#define FOR( typeX,x,valueX,  typeY,y,valueY,  condition, increments) typeX x; typeY y; for(x=valueX,y=valueY;condition;increments)

FOR(int,i,0,  int,f,0.0,  i < 5, i++)
{
  //...
}

Denken Sie daran, dass sich Ihre variablen Bereiche auch auf diese Weise nicht in der for-Schleife befinden.

Ryan Favale
quelle
Sie können diese Einschränkung leicht überwinden, indem Sie den Code mit {und in einem separaten Bereich in das Makro einschließen }.
Nathan Osman
4
Nein, er konnte nicht. Sein Makro umschließt den Schleifenkörper nicht. Er könnte eine zusätzliche öffnende Klammer hinzufügen, aber das würde eine "zusätzliche" schließende Klammer erfordern, wenn das Makro verwendet wird.
John
3
Es ist eine interessante Idee, aber ich würde eher eine der anderen Antworten verwenden, bevor ich darüber nachdenke.
Gregn3
-2

Sie können auch wie unten in C ++ verwenden.

int j=3;
int i=2;
for (; i<n && j<n ; j=j+2, i=i+2){
  // your code
}
Loyola
quelle