You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -228,6 +228,10 @@ int main(int argc, char* argv[]) {
228
228
229
229
---
230
230
231
+
# lvalues & rvalues
232
+
233
+
---
234
+
231
235
## Disclaimer lvalue a rvalue
232
236
233
237
* Koncept lvalue a rvalue prešiel počas života C++ mnohými zmenami
@@ -717,6 +721,24 @@ int main() {
717
721
* Kód vyššie nejde kompilovať
718
722
* Po pridani `const` pred `std::optional` to už skompilovať ide, ale robí sa kópia, keďže typ `std::string` a `std::optional<std::string>` nie sú rovnaké
719
723
724
+
725
+
## Preťaženie funkcie
726
+
727
+
* V C++ môžeme funkcie preťažiť
728
+
729
+
```cpp
730
+
void func() { }
731
+
void func(const std::string& s) { }
732
+
733
+
int main() {
734
+
func();
735
+
func("New string");
736
+
}
737
+
```
738
+
739
+
* Toto je ale problém ak máme veľa nepovinných parametrov
740
+
* Stačí sa pozrieť na štandardnú knižnicu, tam každá funkcia má veľa preťažení
741
+
720
742
---
721
743
722
744
## Automatické tvorenie temporary premenných
@@ -804,7 +826,7 @@ int main() {
804
826
## rvalue referencie
805
827
806
828
* Mechanizmus, ktorý nám toto umožňuje sú rvalue referencie
807
-
* Umožnujú nám rozlíšiť medzi lvalue a rvalue referenciami a teda vieme, ktoré hodnoty možeme move a ktoré musíme kopírovať
829
+
* Umožnujú nám rozlíšiť medzi lvalue a rvalue referenciami a teda vieme, ktoré hodnoty možeme `move`-nuť a ktoré musíme kopírovať
808
830
* Funkcie môžu byť preťažené na tieto referencie
809
831
810
832
---
@@ -915,36 +937,54 @@ private:
915
937
916
938
## copy
917
939
918
-
* Kopírovací konštruktor by mal skopírovať objekt
940
+
###Kopírovací konštruktor by mal skopírovať objekt
919
941
920
942
```cpp
921
943
buffer(const buffer &other)
922
944
: size(other.size)
923
-
, ptr(malloc(size)) {
945
+
, ptr(malloc(size)) {
946
+
if (!ptr) {
947
+
// malloc failed
948
+
throw std::bad_alloc();
949
+
}
950
+
// copy buffer
924
951
memcpy(ptr, other.ptr, size);
925
952
}
926
953
```
927
954
928
955
929
-
* Kopírovací operátor priradenia
956
+
### Kopírovací operátor priradenia
930
957
931
958
```cpp
932
-
buffer &operator=(const buffer &rhs) {
933
-
if (&rhs == this)
959
+
buffer& operator=(const buffer& rhs) {
960
+
// self-assignment check
961
+
if (this == &rhs)
934
962
return *this;
935
963
964
+
void* new_ptr = nullptr;
965
+
966
+
if (rhs.size > 0) {
967
+
new_ptr = malloc(rhs.size);
968
+
if (!new_ptr) {
969
+
// malloc failed
970
+
throw std::bad_alloc();
971
+
}
972
+
memcpy(new_ptr, rhs.ptr, rhs.size);
973
+
}
974
+
975
+
// Uvoľni staré dáta až po úspešnom alokovaní nových
976
+
free(ptr);
977
+
978
+
ptr = new_ptr;
936
979
size = rhs.size;
937
-
ptr = malloc(size);
938
-
memcpy(ptr, rhs.ptr, size);
980
+
939
981
return *this;
940
982
}
941
983
```
942
984
943
-
* Rátame s tým, že `malloc` sa nemôže pokaziť, bežne by sme to riešili
944
-
945
985
---
946
986
947
-
## move
987
+
## `move`
948
988
949
989
* Move konštruktor a operátor priradenia by mali použiť so "starého" objektu najviac ako sa dá
950
990
@@ -1067,9 +1107,9 @@ int main() {
1067
1107
}
1068
1108
```
1069
1109
1070
-
* Ak sa z funkcie vracia lokálna premenná rovnakého typu ako je návratový typ tak sa urobí automatický move
1071
-
* Táto premenná prestáva existvať (je lokálna), takže dáva zmysel ju movnuť
1072
-
* Toto je definované v štandarde
1110
+
* Ak sa z funkcie vracia lokálna premenná rovnakého typu ako je návratový typ tak sa urobí automatický *move*
1111
+
* Táto premenná prestáva existvať (je lokálna), takže dáva zmysel ju *move*-nuť
1112
+
* Toto je definované v štandarde a môžeme sa na to spoľahnúť
1073
1113
1074
1114
---
1075
1115
@@ -1107,16 +1147,16 @@ std::string s = std::get<2>(a);
1107
1147
1108
1148
```cpp
1109
1149
auto a = more();
1110
-
int i = std::get<int>(a); // fails to compile
1150
+
int i = std::get<int>(a); // fails to compile, more than one int
1111
1151
std::string s = std::get<std::string>(a); // OK
1112
1152
```
1113
1153
1114
1154
1115
1155
## `std::tie`
1116
1156
1117
-
* Používa sa na automatické naviazanie premenných z ntice
1118
-
* Urobí vlastne nticu lvalue referencií
1119
-
* Všetko sa movne ak sa dá
1157
+
* Používa sa na automatické naviazanie premenných z n-tice
1158
+
* Urobí vlastne n-ticu lvalue referencií
1159
+
* Všetko sa move-ne ak sa dá
1120
1160
1121
1161
```cpp
1122
1162
std::tuple<int, int, std::string> more() {
@@ -1134,7 +1174,7 @@ int main() {
1134
1174
1135
1175
## `std::ignore`
1136
1176
1137
-
* Ak nás niektorá hodnota z ntice nezaujíma, môžeme použiť `std::ignore`
1177
+
* Ak nás niektorá hodnota z n-tice nezaujíma, môžeme použiť `std::ignore`
1138
1178
* Niekedy sa používa aj na potlačenie upozornení, ktoré vyskočia z atribútu `[[nodiscard]]`, ale to nie je úplne špecifikované
1139
1179
1140
1180
```cpp
@@ -1239,8 +1279,8 @@ int main() {
1239
1279
* Temporaries
1240
1280
* Literals
1241
1281
* xvalues sú eXpiring values (hodnoty, ktoré nám ďalej už netreba)
1242
-
* xvalues sú vytvorené pomocou predtypovania na `&&`, alebo pomocou `std::move, čo je vlastne vnútorne cast
1243
-
* Môžeme teda vyvolať move explicitne
1282
+
* xvalues sú vytvorené pomocou predtypovania na `&&`, alebo pomocou `std::move`, čo je vlastne vnútorne cast na `T&&`
1283
+
* Môžeme teda vyvolať *move* explicitne
1244
1284
1245
1285
1246
1286
## Použitie xvalues
@@ -1262,7 +1302,7 @@ int main() {
1262
1302
## `std::unique_ptr`
1263
1303
1264
1304
*`std::move` sa hodí na použitie s `std::unique_ptr`
1265
-
* Pomocou move sa odstránia dangling pointre, ktoré by mohli vzniknúť ak použijeme copy
1305
+
* Pomocou *move* sa odstránia dangling pointre, ktoré by mohli vzniknúť ak použijeme copy
1266
1306
1267
1307
```cpp
1268
1308
std::unique_ptr<std::string> ptr;
@@ -1299,8 +1339,8 @@ struct S {
1299
1339
1300
1340
## Automatické generovanie
1301
1341
1302
-
* Kompilátor vygeneruje move konštruktor a move operátor priradenia iba ak je to na 100% bezpečné
1303
-
* Ak neexistuje `user defined` kopírovací konštruktor
1342
+
* Kompilátor vygeneruje *move konštruktor* a *move operátor priradenia* iba ak je to bezpečné
1343
+
* Ak neexistuje `user defined` kopírovací konštruktor alebo operátor priradenia
1304
1344
* Ak neexistuje `user defined` deštruktor
1305
1345
* Ak chceme vynútiť generovanie použijeme `= default`
1306
1346
@@ -1317,6 +1357,26 @@ private:
1317
1357
```
1318
1358
1319
1359
1360
+
## Ako je to s kopírovacími operáciami?
1361
+
1362
+
* Ako sme hovorili kompilátor vygeneruje kopírovací konštruktor a operátor priradenia vždy automaticky
1363
+
* Existuje ale výnimka
1364
+
* Ak je definovaný `user defined` move konštruktor alebo operátor priradenia, tak sa kopírovacie operácie nevygenerujú
1365
+
* Taký fix štandardu, kde sa snažili zabrániť auto kopírovaniu, ak je definovaný *move*
1366
+
1367
+
1368
+
## Rule of five
1369
+
1370
+
* Ak definujeme jednu z týchto piatich operácií, mali by sme definovať všetky
1371
+
* Kopírovací konštruktor
1372
+
* Kopírovací operátor priradenia
1373
+
* Move konštruktor
1374
+
* Move operátor priradenia
1375
+
* Deštruktor
1376
+
* Definícia je aj keď použijeme `= default` alebo `= delete`
1377
+
1378
+
---
1379
+
1320
1380
## Kanonická implementácia
1321
1381
1322
1382
* Nasledujúce implementácie vygeneruje kompilátor
@@ -1384,7 +1444,7 @@ private:
1384
1444
std::unique_ptr<int> f(std::unique_ptr<int> i) {
1385
1445
std::unique_ptr<int> ret(new int);
1386
1446
*ret = *i + 1;
1387
-
return ret;
1447
+
return ret; // i will be destroyed here
1388
1448
}
1389
1449
1390
1450
int main() {
@@ -1400,9 +1460,9 @@ int main() {
1400
1460
1401
1461
* Move je všade v štandardnej knižnici
1402
1462
* Vector sa snaží urobiť move, keď sa reallokuje
1403
-
*`push_back` môže movnuť prvky do vectora
1404
-
* Štandardné kontainery (`std::string`, `std::vector`, ...) sa dájú movnuť
1405
-
* Veľa funkcionality funguje automaticky a tam kde sa robila kópia sa od C++11 začal robiť move
1463
+
*`push_back` môže *move*-nuť prvky do vectora
1464
+
* Štandardné kontainery (`std::string`, `std::vector`, ...) sa dajú *move*-nuť
1465
+
* Veľa funkcionality funguje automaticky a tam kde sa robila kópia sa od C++11 začal robiť *move*
1406
1466
* Stačilo prekompilovať novým kompilátorom a mali sme rýchlosť zadarmo
1407
1467
1408
1468
---
@@ -1485,7 +1545,7 @@ int main() {
1485
1545
* Kopírovací a move konštruktor podľa štandardu nemôžu mať side effects
1486
1546
* Objekt sa vytvorí priamo v stacku volajúcej funkcie
1487
1547
* Aplikuje sa ak vraciame z funkcie vždy nový objekt
1488
-
* Povinné v C++17
1548
+
* Povinné v C++17 (pod menom URVO - unnamed RVO)
1489
1549
1490
1550
```cpp
1491
1551
std::string f(int i) {
@@ -1557,24 +1617,29 @@ int main() {
1557
1617
}
1558
1618
```
1559
1619
1560
-
* Funkcia môže mať viacero returnov, ale vždy sa musí vracať tá istá premenná
1620
+
* Funkcia môže mať viacero `return`-ov, ale vždy sa musí vracať tá istá premenná
1561
1621
1562
1622
---
1563
1623
1564
1624
## RVO a NRVO manuál
1565
1625
1566
1626
* Vždy používajte návratovú hodnotu, nie výstupné parametre
1567
1627
* Copy, alebo move nesmú mať side effecty
1568
-
* Nepoužíva sa v debug builde, to že to nevidíte počas debugovania neznamená, že to nebude v release kóde
1569
-
1570
-
1571
-
## RVO a NRVO manuál
1572
-
1628
+
* Nepoužíva sa v debug builde, to že to nevidíte počas debugovania neznamená, že to nebude v release kóde (zavisí od kompilátora a verzie štandardu)
1573
1629
* Preferujme kratšie funkcia (väčšia šanca, že nám to výjde)
1574
1630
* Ak sa dá vracajme buď vždy nový objekt, alebo vždy tú istú lokálnu premennú
1575
1631
* Nepreháňajme to, move je už aj tak dostatočne rýchly
1576
1632
1577
1633
1634
+
## Side effects v copy/move
1635
+
1636
+
* Side effecty v copy/move sú zakázané
1637
+
* Resp. nie zakázané, ale kompilátor môže predpokladať že tam nie sú a optimalizovať podľa toho (RVO/NRVO)
1638
+
* Toto je jedno z dvoch miest, kde je povolené vynechať side effects (druhé sú niektoré optimalizácie pri volaní `new`)
1639
+
* Najlepšie je robiť triedy tak aby sme dodržali takzvanú "Rule of zero" stratégiu (nepísať vlastné copy/move/deštruktory)
1640
+
* Nezabúdať, že niekedy potrebujeme `virtual` deštruktor (toto ale nebýva problém)
0 commit comments