Poate. O să mă gândesc puțin data viitoare când voi întâlni situația și mă voi întoarce la tine.

care

Acesta este un simptom al faptului că C ++ nu vă permite să declarați și să inițializați obiecte separat (precum și, eventual, un design prost). Există și alte limbi în care acest lucru nu este cazul.

Nu cred că are sens ca varianta sau tuplul (și prin extensie, perechea) să aibă constructori impliciți.

Nu este un obiect neinițializat doar un număr de octeți în memorie ale cărui valori nu înseamnă nimic util în contextul oricărui tip care ar trebui să fie obiectul?

În limbile pe care le-am folosit care permit declarații/inițializări separate, codul dvs. nu se va compila decât dacă compilatorul poate dovedi că nu accesați niciodată un obiect neinițializat.

EDIT: De asemenea, C ++ nu vă permite să aveți obiecte neinitializate non-banale. Doar declararea unui obiect va invoca constructorul său implicit.

EDITARE 2: obiecte neinizializate non-banale

De asemenea, C ++ nu vă permite să aveți obiecte neinițializate

Uh, este în mod evident greșit. Un simplu declarat local int i; este un contra-exemplu. Și, desigur, același lucru este valabil și pentru tipurile banale personalizate.

Nu sunt sigur că aș lua în considerare variabilele obiectelor tipurilor primitive. Ai dreptate când vine vorba de tipuri banale.

Standardul C ++ este clar în acest sens: instanțele tipurilor primitive sunt obiecte (§6.6.2/1). Simplificat, cam orice cu o adresă de memorie și un tip este un obiect în C ++. Excepția notabilă este funcțiile, care nu sunt obiecte, chiar dacă se întâmplă să ocupe stocarea.

Nu se poate contesta cu standardul ¯ \ _ (ツ) _/(.

RAII nu face atât de ușor de manevrat.

Acesta este un simptom al faptului că C ++ nu vă permite să declarați și să inițializați obiecte separat

Am spus același lucru.

Ai dreptate - nu am observat asta la prima citire.

Mulțumiri. Îmi place noțiunea de „stare goală”.

În general, cu cât un obiect are mai multe stări, cu atât este mai greu de raționat. Dacă puteți evita o stare goală, vă sugerez să o faceți. Sunt șanse să aveți un fel de problemă XY și există un alt mod de a rezolva problema, în afară de a avea o stare goală. În schimb, urmărește ca obiectele tale să fie mereu consecvente cu sine. Definiți obiectul prin invarianții săi și asigurați-vă că nicio interfață publică a obiectului nu permite încălcarea acelor invarianți.

Dacă tocmai creați un obiect cu o grămadă de câmpuri și o grămadă de getters/setatori pentru aceleași câmpuri, probabil că doriți doar un blob de date mutabil care să fie cel mai bine exprimat printr-un obiect public (care struct este în general utilizat pentru expres). Considera

De obicei fac asta, separ datele de comportament prin colectarea datelor în structuri (de obicei imuabile) și funcționalitate în funcțiile constexpr (pure).

Articol extrem de fascinant despre problemele XY. Nu auzisem niciodată de asta. Mulțumiri.

Ar trebui să îl eliminați pe cel generat automat dacă lucrul pe care clasa dvs. încearcă să îl modeleze nu are o astfel de stare? De asemenea, acest lucru va suna ca o întrebare foarte stupidă, dar cum o fac?

Puteți marca constructorii ca șterse.

/ u/ImmediateAntelope3 are dreptate, dar dacă într-adevăr aveți nevoie, îl puteți marca ca șters sau îl puteți face privat (pre-C ++ 11)

Are sens. Vă mulțumim că ați clarificat asta.

Dacă definiți un alt constructor, obiectul dvs. nu va avea un constructor implicit.

Văd. Dacă doriți amândouă, trebuie să le definiți în mod explicit?

Văd. Vă mulțumim că ați clarificat asta.

Există un compromis fundamental aici.

În primul rând, un constructor implicit facilitează integrarea cu alte biblioteci. De exemplu, dacă doriți să redimensionați memoria pentru un vector, aveți nevoie de construcție implicită pentru obiectele dvs. Dacă trebuie să utilizați operatorul [] pe o hartă, de asemenea.

Dar adăugarea unui constructor implicit în care nu are sens este, de asemenea, o preocupare, deoarece puteți încălca mai ușor invarianții pentru obiect. Și anume, ați făcut obiecte juridice parțial formate în codul dvs.

De exemplu, nu puteți avea, poate, persoane implicite, deoarece nu ar avea sens. Dacă da, acum trebuie să aglomerați TOATE funcțiile din persoana clasei pentru verificarea logică a golului, dacă doriți să recuperați acea siguranță, făcând codul mai complicat de citit. Și mai rău, când adăugați funcții noi, puteți uita să verificați golul, să introduceți erori.

Obiectele care sunt construite implicit într-o stare nedefinită se spune de obicei că sunt parțial formate atunci când sunt construite implicit. Aș evita când este posibil, cu excepția cazului în care este o cerință.

Puteți întârzia întotdeauna construcția unui obiect prin std: optional. În acest fel, puteți construi implicit un opțional și ulterior îl completați cu ceva care are sens. De exemplu. ați putea avea, în cazul vectorului pe care îl redimensionați, un vector> și ar face treaba pentru majoritatea cazurilor de utilizare, cu prețul sacrificării unui pic de spațiu (opțional este mai mare decât T, specificul modulului bine cunoscut optimizări precum codificarea golului opțional în T în sine).

Una peste alta, există compromisuri, dar preferința mea este să nu folosesc un constructor implicit unde nu are sens și să folosesc alternative, cum ar fi opționale, atunci când este nevoie să adaptăm lucrurile. Cu toate acestea, încercați să furnizați constructori impliciți dacă au sens, facilitează integrarea cu alte componente.