Kodėl negalima susieti nuorodą, kuri nėra susieta su laikinu objektu?

Kodėl neleidžiama gauti nuolatinės nuorodos į laikinąjį objektą, kuris getx() funkciją? Akivaizdu, kad tai draudžiama pagal C ++ standartą, tačiau domina tokio apribojimo tikslas, o ne nuoroda į standartą.

 struct X { X ref() { return *this; } }; X getx() { return X();} void g(X  x) {} int f() { const X x = getx(); // OK X x = getx(); // error X x = getx().ref(); // OK g(getx()); //error g(getx().ref()); //OK return 0; } 
  • Akivaizdu, kad objekto eksploatavimo trukmė negali būti priežastis, nes C ++ standartas draudžia permalinką į objektą.
  • Akivaizdu, kad laikinasis objektas nėra pastovus aukščiau pateiktame pavyzdyje, nes leidžiama skambinti į nenuolatines funkcijas. Pavyzdžiui, ref() gali pakeisti laikiną objektą.
  • Be to, ref() leidžia jums apgauti kompiliatorių ir gauti nuorodą į šį laikinąjį objektą ir išspręsti mūsų problemą.

Be to:

Sakoma, kad "laikinojo objekto priskyrimas nuorodai į const reiškia šio objekto tarnavimo laiką", ir "nieko nekalbama apie nenuolatines nuorodas". Mano papildomas klausimas . Ar ši užduotis suteikia laikino objekto naudojimo laiką?

 X x = getx().ref(); // OK 
201
14 окт. Aleksejus Malistovas , spalio 14 d. 2009-10-14 14:01 '09, 14:01, 2009-10-14 14:01
@ 11 atsakymų

Šiame straipsnyje „Visual C ++“ tinklaraštyje apie „rvalue“ nuorodas :

... C + + nenorite, kad netyčia pakeistumėte laikinąjį, bet tiesiogiai nekomercinio nario funkcijos kvietimas į pakeistą rvalue yra aiškus, todėl jis leido ...

Iš esmės neturėtumėte bandyti keisti laikinų pertvarų dėl to, kad jie yra laikini objektai ir mirs bet kuriuo metu. Priežastis, dėl kurios jums leidžiama skambinti ne pastoviais metodais, yra ta, kad jūs galite padaryti tam tikrus „kvailus“ dalykus, kai žinote, ką darote ir yra aiškiai apie juos (pvz., Naudodami reinterpret_cast). Bet jei susieti laikiną nuorodą su ne nuolatiniu ryšiu, galite toliau slinkti ją „amžinai“, kad jūsų manipuliacijos su objektu išnyktų, nes kažkur pakeliui jūs visiškai pamiršote, kad jis buvo laikinas.

Jei buvau tavęs, aš permąstysiu savo funkcijų dizainą. Kodėl g () priima nuorodą, ar parametras pasikeičia? Jei ne, nurodykite nuorodą į „const“, jei taip, kodėl bandote jam suteikti laikinus duomenis, ar jums nepatinka laikinas pokytis? Kodėl getx () vis tiek sugrįžta laikinai? Jei bendrinate su mumis savo faktinį scenarijų ir tai, ką bandote atlikti, galite gauti keletą naudingų patarimų, kaip tai padaryti.

Prieš kalbą ir sukčiavimą kompiliatorius retai išsprendžia problemas - tai paprastai sukelia problemų.

<h / "> Redaguoti: sprendžiant klausimus komentaruose: 1) X x = getx().ref(); // OK when will x die? - Nežinau, ir man nerūpi, nes tai yra tiksliai ką aš turiu galvoje „idėja prieš kalbą“. Kalba sako, kad „laikinieji nariai miršta pareiškimo pabaigoje, nebent jie yra susiję su etalonine konstanta, ir šiuo atveju jie miršta, kai nuoroda viršija.“ Taikant šią taisyklę atrodo, kad X jau yra miręs kito pareiškimo pradžioje, nes jis nėra susietas su const nuoroda (kompiliatorius nežino, kas yra ref ().) Tačiau tai tik prielaida.

2) Aš aiškiai suformulavau tikslą: jums neleidžiama keisti laikinų sekcijų, nes paprasčiausiai nėra prasmės (ignoruojant nuorodas į C ++ 0x rvalue). Klausimas: "Kodėl man leidžiama skambinti ne nuolatiniams nariams?" yra gera, bet aš neturiu geresnio atsakymo nei tas, kurį jau minėjau anksčiau.

3) Na, jei aš teisus apie x X x = getx().ref(); miršta pareiškimo pabaigoje, problemos yra akivaizdžios.

Bet kokiu atveju, remiantis jūsų klausimu ir komentarais, nemanau, kad net ir šie papildomi atsakymai jums tenkins. Čia pateikiamas galutinis bandymas / santrauka: C ++ komitetas nusprendė, kad nėra prasmės keisti laiką, taigi jie draudžia susieti su ne nuolatiniais ryšiais. Galbūt taip pat buvo keletas kompiliatoriaus diegimo ar istorinių problemų, aš nežinau. Tada atsirado konkretus atvejis, ir buvo nuspręsta, kad, nepaisant visko, jie vis tiek leistų tiesiogiai keisti ne-const metodą. Tačiau tai yra išimtis - paprastai negalite keisti laiko. Taip, C ++ dažnai yra keista.

85
14 окт. atsakymas pateikiamas sbk 14 okt. 2009-10-14 14:57 '09, 14:57, 2009-10-14 14:57

Jūsų „ getx() kode grąžinamas laikinas objektas, vadinamasis „rvalue“. Rvales galite kopijuoti į objektus (arba kintamuosius) arba susieti juos su const nuorodomis (kurios prailgina jų tarnavimo laiką iki referencinio šaltinio pabaigos). R reikšmių negalima susieti su ne nuolatinėmis nuorodomis.

Tai buvo sąmoningas sprendimas, kad vartotojai netyčia nekeistų objekto, kuris mirs išraiška:

 g(getx()); // g() would modify an object without anyone being able to observe 

Jei norite tai padaryti, pirmiausia turėsite sukurti vietinę kopiją ar objektą arba susieti jį su „const“ nuoroda:

 X x1 = getx(); const X x2 = getx(); // extend lifetime of temporary to lifetime of const reference g(x1); // fine g(x2); // can't bind a const reference to a non-const reference 

Atkreipkite dėmesį, kad į šį C ++ standartą bus įtrauktos nuorodos „rvalue“. Tai, ką žinote kaip nuorodas, vadinama „nuorodomis į„ lvalue “. Jums bus leista susieti rvales su rvalue nuorodomis ir galite perkrauti funkcijas „rvalue-ness“:

 void g(X // #1, takes an ordinary (lvalue) reference void g(X // #2, takes an rvalue reference X x; g(x); // calls #1 g(getx()); // calls #2 g(X()); // calls #2, too 

Idėja susieti „rvalue“ yra ta, kad, kadangi šie objektai vis dar mirs, galite pasinaudoti šiomis žiniomis ir įgyvendinti vadinamąjį „judėjimo semantiką“, tam tikrą optimizavimą:

 class X { X(X rhs) : pimpl( rhs.pimpl ) // steal rhs' data... { rhs.pimpl = NULL; // ...and leave it empty, but deconstructible } data* pimpl; // you would use a smart ptr, of course }; X x(getx()); // x will steal the rvalue data, leaving the temporary object empty 
31
14 окт. atsakymas pateikiamas sbi 14 okt. 2009-10-14 16:19 '09 at 4:19 PM 2009-10-14 16:19

Tai, ką rodote, yra ta, kad operatorių grandinė yra leidžiama.

  X x = getx().ref(); // OK 

Sąvoka „getx“ (). ref (); ir tai daroma prieš užbaigiant, prieš paskiriant „x“.

Atkreipkite dėmesį, kad getx () negrąžina nuorodos, bet visiškai suformuotas objektas vietiniame kontekste. Objektas yra laikinas, bet tai nėra const, kuris leidžia jums paskambinti kitais būdais, kad būtų galima apskaičiuoti kitų šalutinių poveikių vertę ar atsiradimą.

 // It would allow things like this. getPipeline().procInstr(1).procInstr(2).procInstr(3); // or more commonly std::cout << getManiplator() << 5; 

Pažvelkite į šio atsakymo pabaigą dėl geresnio pavyzdžio.

Jūs negalite susieti laikinos nuorodos, nes tai sukurs nuorodą į objektą, kuris bus sunaikintas išraiškos pabaigoje, tokiu būdu paliekant jums sugadintą nuorodą (kuri yra netvarkinga ir standartas neatrodo netvarkingas).

Vertė, kurią grąžina ref (), yra galiojanti nuoroda, tačiau metodas neatsižvelgia į grąžinto objekto tarnavimo laiką (nes ji negali turėti šios informacijos savo kontekste). Jūs iš esmės ką tik padarėte:

 x = const_cast<x> 

Priežastis, kodėl tai daroma naudojant sąsają su laikinuoju objektu, yra ta, kad standartas pratęsia laikinojo šaltinio resurso ryšį, todėl laikinų objektų tarnavimo laikas viršija operatorių.

Taigi vienintelis likęs klausimas yra tas, kodėl standartas nenori, kad nuoroda į laikinus išteklius pratęstų objekto gyvenimą po instrukcijų pabaigos?

Manau, tai yra dėl to, kad kompiliatorių bus labai sunku tinkamai naudoti laikiniems objektams. Tai buvo padaryta pastoviam ir nuolatiniam naudojimui, nes jis yra ribotas, todėl priverčia jus padaryti objekto kopiją, kad galėtumėte padaryti kažką naudingo, tačiau ji suteikia tam tikrą ribotą funkcionalumą.

Pagalvokite apie šią situaciją:

 int getI() { return 5;} int x = getI(); x++; // Note x is an alias to a variable. What variable are you updating. 

Šios laikinos priemonės galiojimo pratęsimas bus labai painus.
Nors:

 int const y = getI(); 

Suteikia jums kodą, kuris yra intuityvus naudoti ir suprasti.

Jei norite pakeisti vertę, turite grąžinti kintamojo vertę. Jei bandote išvengti „obejct“ kopijavimo iš funkcijos (kaip atrodo, objektas nukopijuojamas atgal) (techniškai). Tada nesijaudinkite, kompiliatorius yra labai geras „Grąžinimo vertės optimizavimas“

16
14 окт. Martino Jorko atsakymas spalio 14 d 2009-10-14 16:45 '09, 4:45 PM 2009-10-14 16:45

Kodėl aptariami dažnai užduodami klausimai apie C ++ ( boldfacing ):

C ++ sistemoje su non-const nuorodomis galima susieti lvales, o const nuorodos gali būti susietos su lvalues ​​arba rvalues, bet nėra nieko, kas gali būti susieta su ne pastovia r reikšme. Taip siekiama apsaugoti žmones nuo laikinų objektų, kurie buvo sunaikinti prieš jų naują vertę, vertybių keitimo . Pavyzdžiui:

 void incr(int a) { ++a; } int i = 0; incr(i); // i becomes 1 incr(0); // error: 0 is not an lvalue 

Jei šis incr (0) buvo išspręstas arba laikinai, kurio niekas niekada nematė, jis nepadidės arba - daug blogiau - vertė 0 taps 1. Paskutinis skamba kvailai, bet iš tikrųjų klaida buvo ankstyvuosiuose Fortran kompiliatoriuose, skiriančiuose atminties elementą įrašymo vertė 0.

8
30 дек. Atsakymą pateikė Tony Delroy, gruodžio 30 d. 2015-12-30 13:05 '16 at 13:05 pm 2015-12-30 13:05

Pagrindinė problema yra ta

 g(getx()); //error 

yra logiška klaida: g pakeičia getx() , bet jūs neturite galimybės išnagrinėti modifikuoto objekto. Jei g nereikėjo pakeisti savo parametro, tuomet nereikėtų naudoti lvalue nuorodos, jis galėtų priimti parametrą pagal vertę arba nuorodą į const.

 const X x = getx(); // OK 

galioja, nes kartais reikia pakartotinai panaudoti išraiškos rezultatą, ir gana aišku, kad susiduriate su laikinu objektu.

Tačiau tai neįmanoma padaryti

 X x = getx(); // error 

galioja, nepadarius g(getx()) galiojančių, kurių pirmiausia stengėsi išvengti kalbos kūrėjai.

 g(getx().ref()); //OK 

galioja, nes metodai žino tik apie pastovumą, jie nežino, ar jie yra vadinami lvalue arba rvalue.

Kaip visada C ++ sistemoje, turite šią problemą spręsti, tačiau turite pranešti kompiliatoriui, kad žinote, ką darote, nes tai yra aiški:

 g(const_cast<x> 
6
13 нояб. Atsakyti Dan Berindei lapkritis 13 2009-11-13 17:39 '09 17:39 2009-11-13 17:39

Atrodo, kad pradinis klausimas, kodėl tai yra nepriimtinas, buvo aiškiai atsakyta: „kadangi tai greičiausiai yra klaida“.

FWIW, manau, kad jums parodysiu, kaip tai padaryti, nors nemanau, kad tai yra geras būdas.

Priežastis, dėl kurios kartais noriu perduoti laikiną vertę metodui, kuris naudoja nenuolatinę nuorodą, yra sąmoningai atmesta nuoroda, kuri neskambina skambinančiu metodu. Kažkas panašaus:

 // Assuming: void Person::GetNameAndAddr(std::string  std::string  string name; person.GetNameAndAddr(name, string()); // don't care about addr 

Kaip paaiškinta ankstesniuose atsakymuose, tai nėra parengta. Tačiau ji kaupia ir veikia tinkamai (su mano kompiliatoriumi):

 person.GetNameAndAddr(name, const_cast<string  string > 

Tai tiesiog rodo, kad jūs galite naudoti liejimą meluoti kompiliatoriui. Akivaizdu, kad būtų daug lengviau deklaruoti ir perduoti nepanaudotą automatinį kintamąjį:

 string name; string unused; person.GetNameAndAddr(name, unused); // don't care about addr 

Šis metodas į metodo taikymo sritį įtraukia nereikalingą vietinį kintamąjį. Jei dėl kokių nors priežasčių norite vėliau išvengti metodo naudojimo, pavyzdžiui, siekiant išvengti painiavos ar klaidų, galite ją paslėpti vietiniame bloke:

 string name; { string unused; person.GetNameAndAddr(name, unused); // don't care about addr } 

- Chris

5
22 февр. Chris Pearson atsakymas vasario 22 d 2013-02-22 03:54 '13, 3:54, 2013-02-22 03:54

Neteisingas sprendimas apima raktinį žodį „mutable“. Tiesą sakant, blogis lieka kaip skaitytojo pratimas. Arba ieškokite čia: http://www.ddj.com/cpp/184403758

4
14 окт. atsakymas pateikiamas paul 14 oct. 2009-10-14 18:02 '09 at 18:02 2009-10-14 18:02

Kodėl jums reikia X x = getx(); ? Tiesiog naudokite X x = getx(); ir pasikliauti RVO.

4
14 окт. DevFred atsakymas spalio 14 d. 2009-10-14 14:08 '09, 14:08, 2009-10-14 14:08

Puikus klausimas, ir čia yra mano bandymas su trumpesniu atsakymu (nes yra daug naudingos informacijos komentaruose ir sunku iškasti jį triukšmo metu).

Bet kuri nuoroda, tiesiogiai susieta su laikinu ryšiu, pratęsia jo gyvenimą [12.2.5]. Kita vertus, nuoroda, inicijuota kita nuoroda, neteks (net jei tai galiausiai yra tas pats laikinas). Tai prasminga (kompiliatorius nežino, ką ši nuoroda galiausiai reiškia).

Tačiau visa ši idėja yra labai nepatogi. Pavyzdžiui. const X = X(); paskutinis, kol nuoroda yra x , bet const X = X().ref(); NE (kas žino, kad ref() tikrai grįžo). Pastaruoju atveju x eilutės sunaikintojas vadinamas šios linijos pabaigoje. (Tai pastebima ne trivialus destruktorius.)

Taigi, atrodo, kad tai paprastai yra paini ir pavojinga (kodėl sudėtingos taisyklės apie objektų gyvenimą?), Bet atrodo, kad reikia bent jau nuorodų į konstantas, todėl standartas nustato tokį elgesį.

[Nuo sbi komentaro]: Atkreipkite dėmesį, kad tai, kad susiejimas su nuoroda į const padidina laikiną gyvenimą, yra išimtis, kuri buvo sąmoningai pridėta (TTBOMK, kad būtų galima optimizuoti rankiniu būdu). Nebuvo pridėta jokių ne nuolatinių ryšių išimčių, nes laiko nuorodos susiejimas su ne nuolatine nuoroda greičiausiai buvo programuotojo klaida.

Visi laikini veiksmai laikomi iki visiškos išraiškos pabaigos. Tačiau, norėdami juos naudoti, jums reikia gudrybės, kaip ir su ref() . Tai teisėta. Atrodo, kad nėra jokios priežasties peršokti papildomo lanko, išskyrus tai, kad jis priminė programuotojui, kad vyksta kažkas neįprasto (būtent valdymo parametras, kurio pakeitimai bus greitai prarasti).

[Kita sbi pastaba]. Priežastis, kodėl „Stroustrup“ suteikia („D E“) draudimą susieti „non-const“ nuorodų vertes, yra ta, kad jei Aleksejus () pakeis objektą (kurį tikitės iš funkcijos, kuri priima ne nuolatinę nuorodą), jis pakeis objektą tai mirs, todėl niekas negali gauti modifikuotos vertės. Jis sako, kad tai greičiausiai yra klaida.

3
Atsakymas yra DS. Balandžio 8 d 2012-04-08 22:26 '12, 22:26 pm 2012-04-08 22:26

"Akivaizdu, kad laikinasis objektas nėra pastovus aukščiau pateiktame pavyzdyje, nes skambučiams leidžiama naudoti nenutrūkstamas funkcijas. Pavyzdžiui, ref () gali pakeisti laikinąjį objektą."

Jūsų pavyzdyje getX () negrąžina X konst., Todėl galite skambinti ref () beveik taip, kaip skambintumėte X (). ref (). Jūs grąžinate ne const ref ir todėl galite skambinti į non const metodus, tai, ką jūs negalite padaryti, priskirti nuorodą į non-const nuorodą.

Kartu su „SadSidos“ komentarais jūsų trys taškai yra neteisingi.

2
14 окт. Atsakyti Patrick Oct 14 2009-10-14 14:41 '09, 14:41 val. 2009-10-14 14:41

Turiu scenarijų, kurį norėčiau jums pasakyti, kur norėčiau, kad aš padarytumėte tai, ką prašo Aleksejus. „Maya C ++“ įskiepyje turiu atlikti šiuos „Sinanigan“, kad gautumėte reikšmę mazgo atribute:

 MFnDoubleArrayData myArrayData; MObject myArrayObj = myArrayData.create(myArray); MPlug myPlug = myNode.findPlug(attributeName); myPlug.setValue(myArrayObj); 

Tai varginantis rašyti, todėl parašiau šias pagalbines funkcijas:

 MPlug operator | (MFnDependencyNode node, MObject attribute){ MStatus status; MPlug returnValue = node.findPlug(attribute,  return returnValue; } void operator << (MPlug plug, MDoubleArray doubleArray){ MStatus status; MFnDoubleArrayData doubleArrayData; MObject doubleArrayObject = doubleArrayData.create(doubleArray,  status = plug.setValue(doubleArrayObject); } 

O dabar galiu rašyti kodą nuo pranešimo pradžios:

 (myNode | attributeName) << myArray; 

Problema ta, kad ji neišskiria už „Visual C ++“ ribų, nes bando susieti iš | operatorius. Norėčiau, kad tai būtų nuoroda, nes šis kodas vadinamas daug kartų ir nenoriu nukopijuoti MPlug. Man reikia tik laikino objekto gyvenimui iki antrosios funkcijos pabaigos.

Na, tai yra mano scenarijus. Aš tiesiog maniau, kad pateiksiu pavyzdį, kur noriu daryti tai, ką aprašo Aleksejus. Sveikinu visus kritikos ir pasiūlymus!

Ačiū.

1
22 июня '13 в 19:51 2013-06-22 19:51 Atsakymą pateikė Robertas Trussardi birželio 22 d., 13 val. 19:51 pm. 2013-06-22 19:51

Kiti klausimai dėl žymų arba Užduoti klausimą