Referenz deklarieren und später initialisieren?

70

Ich habe einen Verweis auf MyOjbect, aber das genaue Objekt hängt von einer Bedingung ab. Also möchte ich so etwas machen:

MyObject& ref; 
if([condition]) 
  ref = MyObject([something]) 
else 
  ref = MyObject([something else]);

Ich kann dies derzeit nicht tun, da der Compiler mir nicht erlaubt, eine Referenz zu deklarieren, aber nicht zu initialisieren. Was kann ich tun, um hier mein Ziel zu erreichen?

user1861088
quelle
3
Wäre das eine Initialisierung von einem temporären? MyObject& ref = MyObject([something]);Dies funktioniert auch ohne die Bedingung: nicht , da Sie eine temporäre Referenz nicht an eine nicht konstante Wertreferenz binden können.
GManNickG
@GManNickG: Dies gilt auch für Zaffy- und Suszterpatt-Antworten?
qPCR4vir
@ qPCR4vir: Ja. Die Frage steht in gewisser Weise immer noch nicht direkt.
GManNickG
Wenn Sie nach einer tatsächlichen Lösung suchen, scrollen Sie nach unten zu stackoverflow.com/a/50909452/1021920
hellow

Antworten:

46

Sie müssen es initiieren. Wenn Sie es jedoch bedingt initialisieren möchten, können Sie Folgendes tun:

MyObject& ref = (condition) ? MyObject([something]) : MyObject([something else]);
Zaffy
quelle
6
Ich kann das nicht tun, weil ich tatsächlich Dinge unter diesen Bedingungen mache. Ich bin hier sehr verwirrt. Das scheint mir ein ziemlich häufiges Szenario zu sein, warum ist es nicht erlaubt? Ist mein Codierungsstil Anti-C ++?
user1861088
4
@ user1861088: "Ich kann das nicht tun, weil ich tatsächlich Dinge unter diesen Bedingungen mache." Warum zeigst du nicht tatsächlich, was du in der Frage versuchst zu tun?
GManNickG
1
Ich wollte damit sagen, dass ich in jeder Bedingung mehrere Codezeilen ausführe, anstatt nur eine Funktion aufzurufen, die einen Wert zurückgibt. Also kann ich nicht verwenden? : Recht?
user1861088
@ user1861088 Könnten Sie die Referenz nicht initialisieren, dann die Bedingung erneut testen und mehrere Codezeilen ausführen? Nach dem Erstellen können Referenzen nicht mehr geändert werden. Referenzen können niemals null sein. Diese beiden Dinge halten Sie davon ab, das zu tun, was Sie in der Frage gepostet haben.
Iheanyi
6
Sie könnten in Betracht ziehen, ein Lambda zu verwenden.
Sergiol
18

AFAIK das kann nicht mit einer Referenz gemacht werden. Sie müssten einen Zeiger verwenden:

MyClass *ptr;

if (condition)
    ptr = &object;
else
    ptr = &other_object;

Der Zeiger verhält sich ähnlich wie eine Referenz. Vergessen Sie nicht, ->für den Mitgliederzugriff zu verwenden .

0x499602D2
quelle
5
Sie können eine Referenz einfach deklarieren, als MyClass &ref = *ptrob Sie sie in eine Referenz verwandeln möchten ...
poizan42
Wenn Sie es als Zeiger deklarieren, wird der Fall, dass rvalue die Rückkehr einer Funktion ist, nicht gelöst
Mystic Odin
Nein, es verhält sich nicht ähnlich wie eine Referenz, da es auf dem Heap abgelegt wird und manuell gelöscht werden muss.
Josu Goñi
1
@ JosuGoñi Weder ist wahr. Nur wenn ich Speicher zuweisen würde (via new). ptrzeigt auf Objekte, die auf dem Stapel zugeordnet sind.
0x499602D2
@ 0x499602D2 Er muss new verwenden, es sei denn, er kann einen Verschiebungskonstruktor verwenden.
Josu Goñi
17

Das kannst du nicht machen. Referenzen müssen an etwas gebunden sein, es mag Ihnen vielleicht nicht gefallen, aber es verhindert eine ganze Klasse von Fehlern, denn wenn Sie eine Referenz haben, können Sie immer davon ausgehen, dass sie an etwas gebunden ist, im Gegensatz zu einem Zeiger, der null sein könnte.

Ihr Beispielcode würde sowieso nicht funktionieren, da Sie versuchen, einen nicht konstanten Verweis an ein temporäres Objekt zu binden, das ungültig ist.

Warum brauchen Sie es überhaupt als Referenz? Eine Lösung wäre, sicherzustellen, dass Ihr Typ über einen kostengünstigen Standardkonstruktor verfügt und effizient verschoben werden kann. Führen Sie dann einfach Folgendes aus:

MyObject obj; 
if([condition]) 
  obj = MyObject([something]) 
else 
  obj = MyObject([something else]);

Andernfalls müssten Sie den bedingten Code in eine oder mehrere Funktionen einfügen:

const MyObject& ref = createObject([condition]);

oder

const MyObject& ref = [condition] ? doSomething() : doSomethingElse();

Beachten Sie, dass beide Versionen eine const- Referenz verwenden, die an eine temporäre Referenz gebunden werden kann. Wenn das Objekt nicht const sein muss, beenden Sie erneut den Versuch, eine Referenz zu verwenden:

MyObject obj = createObject([condition]);

Dies wird dank der Optimierung des Rückgabewerts wahrscheinlich genauso effizient sein wie das, was Sie versucht haben

Jonathan Wakely
quelle
Dies sollte die bevorzugte Antwort sein!
Ashley Duncan
12

In C ++ können Sie eine Referenz nicht ohne Initialisierung deklarieren. Sie müssen es initialisieren.


quelle
1
Ich verstehe das. Also irgendwelche Ratschläge zu einem Rundgang? Grundsätzlich brauche ich die Referenz in einem größeren Umfang als die Bedingung.
user1861088
@ user1861088 In diesem Fall entweder 1. Ihren Code neu gestalten (bevorzugt), 2. einen Zeiger verwenden (nicht bevorzugt).
1
3. Verwenden Sie keine Referenz, sondern nur ein Objekt
Jonathan Wakely
@ user529758 ist es wahr für die in der Klasse deklarierte Referenzvariable? class test{ test(param o ):ref(o){}myobj& ref;}
RaGa__M
12

Kurze Antwort: Sie nicht.

Etwas längere Antwort: Mach so etwas:

MyObject& getObject()
{
    if([condition]) 
        return [something] 
    else 
        return [something else];
}

MyObject& ref = getObject();

Übliche Haftungsausschlüsse bezüglich Referenzen gelten selbstverständlich.

suszterpatt
quelle
1
Ja, ich habe darüber nachgedacht, aber ... es ist nur seltsam, dass ich das alles tun muss, um ein scheinbar einfaches Ziel zu erreichen :(
user1861088
Warum wird getObject()eine Referenz zurückgegeben? Worauf bezieht es sich?
Jonathan Wakely
& Jonathan: Das fällt unter den "üblichen Haftungsausschluss". ;)
Suszterpatt
1
@ user1861088 Ihr scheinbar einfaches Ziel ist vom Standard nicht erlaubt. Es widerspricht der Definition einer Referenz. Sie könnten genauso gut fragen, warum Sie einen Stein nicht einfach in die Luft werfen und ihn nicht zu Boden fallen lassen können. Das ist einfach richtig - werfen Sie einfach den Stein und er bleibt an Ort und Stelle.
Iheanyi
11

Was ich gerne mache, ist ein Lambda, das sofort ausgeführt wird.

Nehmen wir an, wir wollen eine const std :: string & zu einer Variablen unter der Karte - wenn die Karte keinen bestimmten Schlüssel enthält - wollen wir werfen.

int main()
{
  std::map<std::string, std::string> myMap = {{"key", "value"}};

  const std::string& strRef = [&]()->const std::string& {
    try {
      return myMap.at("key"); // map::at might throw out_of_range
    }
    catch (...) {
      // handle it somehow and/or rethrow.
    }
  }(); // <- here we immediately call just created lambda.
}

Sie können auch std :: invoke () verwenden , um die Lesbarkeit zu verbessern (seit C ++ 17).

int main()
{
  std::map<std::string, std::string> myMap = {{"key", "value"}};

  const std::string& strRef = std::invoke([&]()->const std::string& {
    try {
      return myMap.at("key"); // map::at might throw out_of_range
    }
    catch (...) {
      // handle it somehow and/or rethrow.
    }
  });
}
Latawiec
quelle
2
Ja. Wenn nur ppl eine Frage beantworten würde, anstatt lieber vorzuschlagen, sollte die Frage nicht gestellt werden. Dies ist wahrscheinlich die sauberste On-the-Fly-Methode, die sich tatsächlich mit dem OP befasst. +1. Leute sollten öfter nach unten scrollen .. lol
violet313
5
MyClass *ptr;

if (condition)
    ptr = &object;
else
    ptr = &other_object;

MyClass &ref = *ptr;
Anonym
quelle
3
Wenn Sie antworten, sollten Sie eine Erklärung des von Ihnen geschriebenen Codes schreiben. Diese Antwort fügt der sehr alten Frage absolut nichts Neues hinzu .
Ajean
1
Dies sieht dem Code aus dieser Antwort sehr, sehr ähnlich .
Artjom B.
Obwohl das Fehlen einer Erklärung schlecht ist, ist dies immer noch die einzige wirkliche Antwort, die nichts extra einführt oder Sie auf das beschränkt, was in einem ternären Operator geschrieben werden kann ...
poizan42
1
@ArtjomB. Diese Antwort schlägt nicht das MyClass &ref = *ptr; am Ende vor, das hier die Schlüsselzeile ist, die später weitere Dereferenzen vermeidet, weiter kommentiert unter: stackoverflow.com/a/62793754/895245
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功
0

if ([Bedingung]) MyObject & ref = MyObject ([etwas]); sonst MyObject & ref = MyObject ([etwas anderes]);

VIPK
quelle
Können Sie erklären, warum dies besser ist als die akzeptierte Antwort?
Einfach Ged
2
Dann können Sie ref nicht außerhalb des Bereichs des if-Blocks verwenden.
John Gowers
0

Normalerweise mache ich das (C ++ 11 oder höher):

std::shared_ptr<ObjType> pObj;
if(condition)
    pObj = std::make_shared<ObjType>(args_to_constructor_1);
else
    pObj = std::make_shared<ObjType>(args_to_constructor_2);

Dies ist sauber und ermöglicht die Verwendung der Objektdefinition mit (möglicherweise unterschiedlichen) Konstruktionen. Dies können Sie nicht direkt mit Zeigern tun, da sich der Compiler über die Verwendung temporärer Objekte beschwert.

M. Khaled
quelle
-2

Sie können das Schlüsselwort "extern" verwenden: Zum ersten Mal (vorausgesetzt, in der Header-Datei) können Sie Ihre Variable vor der Deklaration mit dem Schlüsselwort "extern" deklarieren. Später (in der Quelldatei) wiederholen Sie die Deklaration ohne "extern" und weisen ihr einen Wert zu.

fk0
quelle
4
Welche Frage haben Sie versucht zu beantworten?
JWW