C++17
Zadnja standardizacija jezika C++ dogodila se 2017. godine, a nova se očekuje 2020.
Standard iz 2017. godine je donio niz novosti u jeziku i standardnoj biblioteci
(za popis vidjeti wikipediju). Za testiranje novih svojstava jezika koristimo gcc prevodilac
verzije 8.2 koji instaliran u /usr/local/gcc-8.2
.
Kako bismo mogli koristiti novi prevodilac moramo postaviti neke staze što je napravljeno u datoteci setPath.sh. Datoteku treba učiniti izvršnom i izvršiti:
chmod u+x setPath.sh
./setPath.sh
auto
deklaracija varijabli raspakiravanjem
Grupa varijabli se može auto
deklarirati i inicijalizirati raspakiravanjem
objekta tipa std::pair
, std::tuple
, struct
, std::array
itd.
tuple<short,int,long> t1{1,2,3};
auto [s,i,l] = t1;
auto & [sr,ir,lr] = t1;
sr++;
assert(sr == get<0>(t1));
Moguće je primijeniti i na polja fiksne dimenzije:
double point[2]{1.1, 5.3};
auto & [x,y] = point;
cout << x << "," << y << endl;
To je naročito zgodno u petljama:
map<std::string, size_t> objekti {
{"A", 700}, {"B", 1786}, {"C", 24}, {"D", 108},
};
for (const auto &[species, count] : objekti) {
std::cout << "Broj objekata tipa " << species
<< " = " << count <<".\n";
}
Isto je moguće sa strukturama s javnim varijablama:
struct Employee{
string name;
string role;
unsigned int salary;
};
// ...
vector<Employee> tve;
ve.push_back({"A.A", "W", 1000});
ve.push_back({"B.B", "E", 10000});
for(auto const & [ name, role, salary] : ve)
cout << name << "," << role << "," << salary << endl;
Dedukcija tipa parametriziranih klasa
Paramtri predloška klase mogu se deducirati iz argumenata predanih konstruktoru. Možemo pisati:
pair p{1.2,7}; // deducira pair<double,int>
tuple t{"v", 1u, 2.f}; // deducira tuple<const char *, unsigned int, float>
vector vv(3, 2.0); // deducira vector<double>
Prije C++-17 smo za to morali koristiti pomoćne funkcije (kao na primjer,
make_pair
, make_tuple
i slično).
U nekim situacijama treba sugerirati način dedukcije parametara predloška i tada se pišu deduction guides.
Redukcija dosega varijable u if
i switch
naredbama
Naredbe if
i switch
mogu sadržavati u sebi inicijalizaciju varijable.
Doseg te varijable je cijela if
odnosno switch
naredba.
Primjer: if
naredba.
set<char> sc{'a','b','c'};
char c{'c'};
if (auto it = sc.find(c); it != sc.end()) {
cout << "Znak " << c << " je u skupu na poziciji.\n";
} else {
// it = sc.end()
cout << "Znak " << c << " nije u skupu.\n";
}
// it nije više definiran
Primjer: switch
naredba.
switch (char c = getchar(); c) {
case 'a': putchar('.'); break;
case 's': putchar('-'); break;
default: putchar('+');
}
Posebno je korisno u ovakvom kontekstu gdje destrukcija objekta ima dodatne efekte.
shared_ptr<int> sp(new int{5});
weak_ptr<int> wp = sp;
// ..
if (auto sp1 = wp.lock(); sp1 != nullptr) {
// sp1 i sp još postoje
} else {
// sp1 dohvatljiv, ali je nullptr; sp ne postoji
}
// sp1 nije dohvatljiv
Filesystem
C++17 nudi metode za rad sa datotečnim sustavom koje su neovisne o operacijskom sustavu na kojem se program izvršava.
Klase i funkcije za rad sa datotečnim sustavom nalaze se u imeniku std::filesystem
u zaglavlju <filesystem>
.
Ako koristimo filesystem onda pri pozivu prevodioca moramo proslijediti opciju za povezivanje s
filesystem bibliotekom. Kod gcc prevodioca to je -lstdc++fs
, a za clang to je -lc++fs
.
Klasa std::filesystem::path
Konstruktor kreira stazu iz stringa. Razumije relativne i apsolutne staze.
Metode članice i globalne funkcije:
-
operator ispisa na izlazni stream (ispis u navodnicima);
-
metode članice
c_str()
istring()
koje vraćaju stazu u obliku stringa; -
metoda članica
filename()
daje samo ime datoteke (bez staze do datoteke); -
metoda članica
stem()
daje ime datoteke bez ekstenzije; -
metoda članica
parent_path()
daje stazu do datoteke; -
globalna funkcija
path canonical(path)
koja stazu dovodi u kanonsku formu; -
globalna metoda
exists()
ispituje da li staza (direktorij, datoteka) postoji u datotečnom sustavu. -
globalni operator
/
je preopterećen za konkatenaciju staza; -
globalna metoda
current_path()
daje stazu do radnog direktorija.
Primjer:
using namespace std;
using namespace filesystem;
path p1 =current_path() / "structured_bindings.cpp";
cout << p1 << endl;
// Funkcije članice klase path
path filename = p1.filename();
path stem = p1.stem();
path pp = p1.parent_path();
path rp = p1.relative_path();
cout << "p1 = " << p1 << endl;
cout << "p1.filename() = " << filename << endl;
cout << "p1.stem() = " << stem << endl;
cout << "p1.parent_path() = " << pp << endl;
cout << "p1.relative_path() = " << rp << endl;
std::filesystem::directory_iterator
Za iteriranje kroz direktorij koristimo std::filesystem::directory_iterator
.
End-terator se dobiva pomoću dodijeljenog konstruktora. Dereferenciranjem
iteratora dobivamo objekt tipa std::filesystem::directory_entry
.
for(auto entry : directory_iterator{dir}){
cout << entry.path().filename().string() << " ";
if(entry.is_directory())
{ cout << " je direktorij\n"; }
else if (entry.is_regular_file())
{ cout << " je datoteka.\n "; }
}
std::filesystem::directory_entry
ima niz metoda za ispitivanje datoteke/direktorija:
exists
,
is_block_file
,
is_character_file
,
is_directory
,
is_fifo
,
is_other
,
is_regular_file
,
is_socket
,
is_symlink
,
file_size
,
hard_link_count
,
last_write_time
, status
.
Pomoću metode status()
dobivamo std::filesystem::file_status
objekt koji ima metode
type()
i permissions()
. Metoda permissions()
vraća std::filesystem::perms
objekt
koji predstavlja dozvole pristupa datoteci/direktoriju. Radi se o cjelobrojnom tipu na kojem su definirani
samo operatori bit-po-bit te konstante.
Ova petlja ispisuje dozvole pristupa kao i naredba ls -l
.
for(auto entry : directory_iterator{dir}){
auto entry_status = entry.status();
auto eperms = entry_status.permissions();
if( (eperms & perms::owner_read) != perms::none ) cout << "r";
else cout << "-";
if( ( eperms & perms::owner_write) != perms::none ) cout << "w";
else cout << "-";
if( (eperms & perms::owner_exec) != perms::none ) cout << "x";
else cout << "-";
if( (eperms & perms::group_read) != perms::none ) cout << "r";
else cout << "-";
if( (eperms & perms::group_write) != perms::none ) cout << "w";
else cout << "-";
if( (eperms & perms::group_exec) != perms::none ) cout << "x";
else cout << "-";
if( (eperms & perms::others_read) != perms::none ) cout << "r";
else cout << "-";
if( (eperms & perms::others_write) != perms::none ) cout << "w";
else cout << "-";
if( (eperms & perms::others_exec) != perms::none ) cout << "x ";
else cout << "- ";
cout << entry.path().filename().string() << "\n";
}