15 zmiany C++11 C++14 C++17, C vs C++, Multth, smartP, wsl, CodeBlocks, Linki

zmiany i nowości :
C++11 
C++14
C++17
C vs C++
64 sposoby na crash
Zwracanie wielu wartości
Multithread
Propozycje zmian w języku C++
WSL2
CodeBlocks C++17
maszyna stanów std::variant
CppCoreGuidelines
Big endian vs Little endian
In-class Member Initialisation: From C++11 to C++20
Linki:

...................
C++11 

auto
Lista inicjalizatorów  {}
nullptr
Range for 
Override, final
Strongly-typed Enums (enum class )
Smart pointers
Lambdas
Usprawnienie konstruowania obiektów (delegaci)
Typ 'long long int'
static_assert (predykat)
Wyrażenia stałe (constexpr)
Semantyka przenoszenia (Reguła zera)
Ustawianie metod jako default lub delete
Ułatwienie używania wątków
Szablony ze zmienną listą parametrów
std::atomic dla współbieżności, volatile dla pamieci specjalnej



auto (decltype)
Słowo kluczowe auto w standardzie C++11 zmieniło swoje znaczenie w stosunku do standardu C++. Słowo kluczowe auto wg standardu C++11 oznacza zastępczy typ zmiennej, który zostanie wydedukowany na podstawie wartości za pomocą której zmienna zostanie zainicjalizowana.

Zmienna, której nadano zastępczy typ auto musi zostać zainicjalizowana w chwili jej tworzenia. W przeciwnym wypadku zostanie zwrócony błąd kompilacji.

Tworzona zmienna może zawierać modyfikatory takie jak const i volatile. Zmienna może być również wskaźnikiem (*), referencją (&) czy też referencją do r-wartości (&&).


Na zmiennych o typie zastępczym auto można używać słów kluczowych sizeof i typeid. Nie można natomiast odczytywać informacji za pomocą wspomianych słów kluczowych bezpośrednio ze słowa kluczowego auto - wystąpi wówczas błąd kompilacji.

Typ czegokolwiek, w szczególności typ wartości zwracanej przez funkcję, nie jest łatwy do wyrażenia dlatego C++11 pozwala na uproszczenie tego problemu na dwa sposoby. Po pierwsze, w definicji zmiennej z jawnym inicjowaniem można użyć słowa kluczowego auto. Można w ten sposób utworzyć zmienną o typie takim, jak typ inicjującej wartości:

auto trudnyDoOkresleniaTypZmiennej = std::bind(&JakasFunkcja, _2, _1, jakisObiekt);
auto innaZmienna = L"To jest tekst";

Typem trudnyDoOkresleniaTypZmiennej może być cokolwiek zwracanego przez pewną funkcję szablonową pod boost::bind dla danych argumentów. Typ ten jest łatwy do określenia przez kompilator, natomiast dla użytkownika jest to trudne.

auto jest także przydatne przy ograniczaniu rozwlekłości kodu. Na przykład w range for.

Dodatkowo, słowo kluczowe decltype może być zastosowane w celu określenia typu w czasie kompilacji. Przykładowo: auto jakisInt=11;


decltype(jakisInt) innaZmiennaInt = 5;



auto (opóźniona deklaracja typu zwracanego)
Użycie auto przed nazwą funkcji nie ma nic wspólnego z dedukcją typów. Zamiast tego wskazuje, że użyta została składnia C++11 dla opóźnionej deklaracji typu zwracanego,
co oznacza, że typ zwracany przez funkcję zostanie zadeklarowany

za listą parametrów (za „->”).


template<typename Container, typename Index>       // wersja C++11
auto   authAndAccess(Container&& c, Index i)  -> decltype(std::forward<Container>(c)[i])


Standard C++11 dopuszcza dedukcję zwracanego typu dla wyrażeń lambda obejmujących jedną instrukcję, a standard C++14 rozszerza te możliwości na wszystkie wyrażenia lambda i wszystkie funkcje, w tym te z wieloma instrukcjami. W przypadku funkcji authAndAccess oznacza to, że w języku C++14 możemy pominąć opóźnioną deklarację typu zwracanego, pozostawiając tylko początkowe słowo kluczowe auto. W tej formie deklaracji auto oznacza, że ma miejsce dedukcja typów. W szczególności oznacza, że kompilatory wydedukują typ zwracany przez funkcję

na podstawie jej implementacji:


template<typename Container, typename Index>     // wersja
decltype(auto)                                                                //
authAndAccess(Container&& c, Index i)                   // C++14
{
authenticateUser();
return std::forward<Container>(c)[i];

}


Lista inicjująca (nie lista inicjalizacyjna )
C++11 posiada składnię w pełni ujednolicającą inicjowanie dowolnych typów.
struct PodstStrukt
{
 int x;
 float y;
};

struct AlternatStrukt
{
  AlternatStrukt(int _x, float _y) : x(_x), y(_y) {}

private:
  int x;
  float y;
};

PodstStrukt zm1{5, 3.2f};
AlternatStrukt zm2{2, 4.3f};

Zainicjowanie zm1 działa dokładnie tak, jakby to była lista inicjująca z C. Każda publiczna zmienna będzie inicjowana przez każdą odpowiadającą wartość z listy inicjującej. Niejawne konwersje typów będą przeprowadzane w razie potrzeby i jeśli nie jest dostępna konwersja typów, wtedy kompilator zgłosi błąd kompilacji.

Zainicjowanie zm2 po prostu wywołuje konstruktor.



nullptr
W starszym C++, stała 0 spełnia dwie funkcje: stałej całkowitej i pustego wskaźnika.
W C NULL jest makrem preprocesora zdefiniowanym jako ((void*)0) lub 0.
W C++ niejawna konwersja z void* do wskaźnika innego typu jest niedozwolona, więc nawet takie proste przypisanie jak char* c = NULL mogłoby być w tym przypadku błędem kompilacji.

nullptr nie może być przypisane do typów całkowitych, ani porównywane z nimi; może być porównywane z dowolnymi typami wskaźnikowymi.



Range-based for loops


for(const auto& kvp : map) 
{
  std::cout << kvp.first << std::endl;

  for(auto v : kvp.second)
  {
     std::cout << v << std::endl;
  }
}

int arr[] = {1,2,3,4,5};
for(int& e : arr) 
{
  e = e*e;
}



Override


class B 
{
public:
   virtual void f(int) {std::cout << "B::f" << std::endl;}
};

class D : public B
{
public:
   virtual void f(int) override final {std::cout << "D::f" << std::endl;}
};

class F : public D
{
public:
   virtual void f(int) override {std::cout << "F::f" << std::endl;}
};

dodanie override sprawia, że metoda musi nadpisywać, jeśli mamy literówkę i nie nadpisuje virtualnej to dostaniemy błąd.
Final - funkcja zadeklarowana jako final nie może byc nadpisana

Przykład gdzie wstawienie ovveride skonczy się błędem

class B 
{
public:
   virtual void f(short) {std::cout << "B::f" << std::endl;}
};

class D : public B
{
public:
   virtual void f(int) override {std::cout << "D::f" << std::endl;}
};

metoda przeciążona, więc inna -błąd kompilacji.


Strongly-typed enums
W starszym C++ typy wyliczeniowe nie są bezpieczne typowo (ang. type-safe). Są one de facto typami całkowitymi.
C++11 zezwala na taką specjalną klasyfikację typów wyliczeniowych:

enum class Enumeration
{
  Val1,
  Val2,
  Val3 = 100,
  Val4           /* = 101 */,
};

Takie wyliczenie jest bezpieczne typowo. Wartości klasy enum nie są niejawnie konwertowane na typy całkowite i nie mogą zatem być porównywane z typami całkowitymi. Przykładowo:

Enumeration::Val4 == 101
spowoduje błąd kompilacji.

Dodatkowo C++11 zezwala standardowym wyliczeniom na podawanie jawnych zasięgów jednocześnie z definicją typu podstawowego:

enum Enum3 : unsigned long {Val1 = 1, Val2};



Smart pointers
ostatni podpunkt w tym rozdziale:  LINK



Lambdas (wyrażenia lambda, funkcje lambda, domknięcia, funkcje anonimowe) 
Funkcja definiowana w miejscu jest stosowana

[](int x, int y) { return x + y; }

Podstawowa struktura wyrażeń lambda wygląda tak:

[ lista przechwytywania ](lista parametrów) -> deklaracja_zwracanego_typu { ciało lambdy}

nawias kwadratowy na początku nazywany jest zapowiedzią lambdy. Zgodnie z nazwą zapowiedź lambdy oznacza początek wyrażenia lambda.
Zapowiedź może też obejmować listę przechwytywania.
Lista przechwytywania jest ważna, ponieważ to na niej wymienione są wszystkie zmienne z zasięgu
zewnętrznego, które mają być dostępne w ciele lambdy. Określa się tu też, czy mają być one przechwytywane
za pomocą wartości (kopiowania), czy referencji. Ta lista tworzy więc domknięcie wyrażenia lambda.

[&] - referencje do wszystkich objektow
[=] - kopie do wszystkich obiektow
[ ] - lapie kontekst (closure costam)
[] - niczego nie lapie

jeśli w Capture List [ ] mamy &s, przekazuje przez referencję zmienną s która jest poza linią w której definiujemy lambdę
ale równie dobrze mogło by być samo [ &] ale wtedy daje dostęp do wszystkich obiektów

Oto przykładowa definicja wyrażenia lambda:

[](const double multiplicand, const double multiplier)
{ return multiplicand * multiplier; }

To wcześniejsza funkcja mnożenia przedstawiona jako lambda. Zapowiedź obejmuje pustą listę
przechwytywania, co oznacza, że nie są używane żadne dane z zewnętrznego zasięgu.
 Ponadto nie jest tu podany typ zwracanej wartości, ponieważ kompilator może łatwo go określić.
-> deklaracja_zwracanego_typu




Usprawnienie konstruowania obiektów (delegaci)
W C++03 konstruktory nie mogą wywoływać innych konstruktorów; każdy konstruktor musi sam konstruować wszystkie składowe klasy lub wywołać metody tej samej klasy.
C++11 pozwala na wywołanie innych równorzędnych konstruktorów (znanych jako delegaci). To pozwala na wykorzystanie cech innego konstruktora za pomocą niewielkiego dodatku kodu.



Typ 'long long int'
C++11 wprowadza ten typ do Standardu C++.

Typ long long int jest mniej użyteczny w systemach 64-bitowych, jako że przykładowy model wielkości danych w tych systemach to:

16 bit: short int
32 bit: int
64 bit: long int
Niemniej jednak, w systemach 32-bitowych (a także w 64-bitowych systemach Windows, które używają modelu wielkości danych, gdzie typ long ma długość 32 bitów), jest głęboko zakorzenionym nawykiem używać typu long long int jako całkowitoliczbowej zmiennej 64-bitowej.



static_assert 
 predykat (forma zdaniowa w danym języku, która zwraca prawdę lub fałsz), umieszczony w pewnym miejscu w kodzie. Asercja wskazuje, że programista zakłada, że predykat ów jest w danym miejscu prawdziwy. W przypadku gdy predykat jest fałszywy (czyli niespełnione są warunki postawione przez programistę) asercja powoduje przerwanie wykonania programu. Asercja ma szczególne zastosowanie w trakcie testowania tworzonego oprogramowania, np. dla sprawdzenia luk lub jego odporności na błędy. Zaletą stosowania asercji jest możliwość sprawdzenia, w którym fragmencie kodu źródłowego programu nastąpił błąd.
Nowe narzędzie wprowadza nowy sposób testowania asercji w czasie kompilacji, przy użyciu nowego słowa kluczowego static_assert. Deklaracja przyjmuje następującą formę:

static_assert( stałe_wyrażenie, komunikat_błędu ) ;



constexpr (wyrażenia stałe)
W C++ stałe wyrażenia to wyrażenia takie jak 3+4, które zawsze zwracają ten sam wynik i nie wywołują żadnych dodatkowych efektów ubocznych (ang. side effect). Stałe wyrażenia są dla kompilatorów okazją do optymalizacji, ponieważ kompilatory często wykonują te wyrażenia w czasie kompilacji i wstawiają ich wyniki do programu. Jest wiele miejsc, gdzie specyfikacja C++ wymaga użycia stałych wyrażeń. Są to między innymi definicja tablicy i wartości typów wyliczeniowych. Jednak jeśli wyrażenie zawiera wywołanie funkcji lub wykonanie konstruktora obiektu nie będzie zinterpretowane przez kompilator jako stałe. Na przykład:
int GetFive() {return 5;}

int someValues[GetFive() + 5]; //stwórz tablicę 10 elementów typu ''int''. Jest to błąd w C++.
Zostaje wykryty błąd, ponieważ GetFive() + 5 nie jest wyrażeniem stałym. Kompilator nie potrafi rozpoznać, że GetFive jest stałe w czasie uruchamiania, ponieważ w teorii ta funkcja mogłaby wpływać na globalną zmienną, wywołać funkcje nie będące stałe w czasie uruchamiania, itd.
C++11 wprowadza nowe słowo kluczowe constexpr, które pozwala użytkownikowi na zagwarantowanie, że funkcja lub konstruktor obiektu są stałymi podczas kompilacji. W C++11 powyższy kod można przepisać następująco:
constexpr int GetFive() {return 5;}

int someValues[GetFive() + 5]; //stwórz tablicę 10 elementów typu ''int''. Poprawne w C++11
To pozwala kompilatorowi rozpoznać i zweryfikować, że GetFive jest stałą podczas kompilacji.

Słowo kluczowe constexpr gwarantuje, że wartość zwracana przez funkcję, metodę bądź zmienną jest stała podczas procesu kompilacji. Są to funkcje, które (spełniając pewne warunki) otrzymują prawo do występowania również w wyrażeniach inicjalizujących obiekty z przydomkiem constexpr. Funkcje constexpr tworzymy po to, aby się nimi natychmiast posłużyć do definiowania innych obiektów constexpr.
constexpr Clasa nazwaNowejKlasy = obiekt.metodaa( 88,77);     //metodaa musi być constexpr

Użycie constexpr narzuca pewne ograniczenia na budowę funkcji. Ograniczenia te są następujące:
-funkcja musi zwracać wartość, więc nie może zwracać typu void;
-funkcja nie może deklarować żadnych zmiennych ani definiować nowych typów danych;
- ciało funkcji może zawierać tylko pojedynczą instrukcję return;
- argumenty funkcji muszą gwarantować, że po ich podstawieniu będzie możliwe otrzymanie wyrażenia o stałej wartości.


Zmienne  jako stałowyrażeniowe: ( stosowane: zamiast makr obiektowych)
Standard C++11 wprowadził również możliwość definiowania zmiennych ze słowem kluczowym constexpr. Zmienne, które używają słowa kluczowego constexpr muszą zostać zainicjalizowane podczas ich tworzenia wartością stałą lub wyrażeniem, które da wartość stałą:

constexpr int DANGEROUS = 1024 + 1024;
stosuj zamiast makr obiektowych     #define DANGEROUS 1024+1024




Semantyka przenoszenia 
C++11 wprowadza nowy typ referencyjny, zwany referencją do r-wartości(r-referencja), definiowany jako typename &&. Jeśli używamy referencji do r-wartości, to "konstruktor przenoszący" (nazwa analogiczna do konstruktora kopiującego w C++) std::vector może za pomocą referencji do r-wartości po prostu kopiować wskaźnik do tablicy z r-wartości, zostawiając r-wartość w stanie pustym. Unikamy wtedy kopiowania całej tablicy, a niszczenie pustego obiektu tymczasowego odbywa się bez dealokacji pamięci. Funkcja zwracająca obiekt tymczasowy typu vector musi zwrócić tylko typ std::vector<>&&. Jeśli obiekt vector posiada konstruktor przenoszący, wtedy ten konstruktor może być wywołany, co zapobiega wielu nadmiarowym operacjom alokacji pamięci.

Oto przykładowe scenariusze, w których kiedyś trzeba było kopiować obiekt zamiast go przenosić:

- Zwracanie lokalnego obiektu jako wartości zwracanej przez funkcję lub metodę. Aby w wersjach
starszych niż C++11 zapobiegać w takich sytuacjach wywoływaniu konstruktora kopiującego,
często stosowano wskaźniki.
- Wstawianie obiektu do kolekcji std::vector lub innych kontenerów.
- Implementowanie funkcji szablonowej std::swap<T>.

W wielu takich sytuacjach obiekt źródłowy nie musi pozostawać nienaruszony, dlatego nie trzeba tworzyć głębokiej (i ze względu na wydajność często kosztownej) kopii, aby taki obiekt pozostał użyteczny. W C++11 wprowadzono mechanizm, dzięki któremu przenoszenie danych przechowywanych w obiekcie stało się podstawową operacją. Oprócz konstruktora kopiującego i kopiującego operatora przypisania autor klasy może teraz pisać konstruktory przenoszące i przenoszące operatory przypisania. Operacja przenoszenia jest zwykle bardzo wydajna. W porównaniu z rzeczywistym kopiowaniem dane obiektu źródłowego są tylko przekazywane do docelowego obiektu, a argument (obiekt źródłowy) operacji przechodzi w stan „pusty” (początkowy).

pokazano fikcyjną klasę z bezpośrednio zaimplementowanymi oboma podejściami:
konstruktorem kopiującym (wiersz 6.), kopiującym operatorem przypisania (wiersz 8.), a także
konstruktorem przenoszącym (wiersz 7.) i przenoszącym operatorem przypisania (wiersz 9.).

03 class Clazz {
04 public:
05 Clazz() noexcept;                                             // Konstruktor domyślny.
06 Clazz(const Clazz& other);                             // Konstruktor kopiujący.
07 Clazz(Clazz&& other) noexcept;                    // Konstruktor przenoszący.
08 Clazz& operator=(const Clazz& other);          // Kopiujący operator przypisania.
09 Clazz& operator=(Clazz&& other) noexcept; // Przenoszący operator przypisania.
10 virtual ~Clazz() noexcept;                                // Destruktor.



Reguła zera”  :(najnowsze podejście) głównym celem każdego dewelopera używającego C++
powinno być to, aby nie deklarować i definiować bezpośrednio tego rodzaju konstruktorów oraz operatorów przypisania. Treść reguły:

Pisz klasy w taki sposób, abyś nie musiał bezpośrednio deklarować i definiować ani destruktora, ani konstruktorów kopiujących i przenoszących, ani kopiujących i przenoszących operatorów przypisania. Do zarządzania zasobami stosuj inteligentne wskaźniki języka C++ oraz klasy i kontenery z biblioteki standardowej.

historyczne reguły (stary c++) reguła trzech: jeśli w klasie bezpośrednio zdefiniowany jest destruktor, prawie zawsze należy zdefiniować też konstruktor kopiujący i kopiujący operator przypisania.
Reguła pięciu (od c++11) : do języka dodano konstruktor przenoszący i przenoszący operator przypisania, dlatego w klasie z definicją destruktora trzeba było definiować także te dwie specjalne funkcje składowe.



Ustawianie metod jako default lub delete
W standardzie C++ kompilator dołącza do obiektów konstruktor domyślny, konstruktor kopiujący, operator przypisujący operator= jeśli użytkownik nie zdefiniuje swoich własnych wersji tych metod. C++ definiuje także kilka globalnych operatorów (takich jak operator= i operator new), które pracują ze wszystkimi klasami i które użytkownik także może zastąpić swoimi wersjami.
C++11 pozwala na jawne użycie lub zaprzestania użycia tych standardowych metod. Na przykład poniższy typ jawnie deklaruje użycie domyślnego konstruktora:

struct SomeType
{
  SomeType() = default; //Domyślny konstruktor jest jawnie określony.
  SomeType(OtherType value);
};

Alternatywnie, pewne cechy mogą być jawnie zablokowane. Na przykład poniższy typ jest niekopiowalny:

struct NonCopyable
{
  NonCopyable & operator=(const NonCopyable&) = delete;
  NonCopyable(const NonCopyable&) = delete;
  NonCopyable() = default;
};

Typ może być niemożliwy do zaalokowania za pomocą operatora new:

struct NonNewable
{
  void *operator new(std::size_t) = delete;
};

Taki obiekt może być alokowany tylko na stosie lub jako składowa innego obiektu. Nie może być alokowany na stercie bez nieprzenośnych sztuczek.

Specyfikator = delete może być użyty do zablokowania wywołania dowolnej metody, co może być użyte do zablokowania wywołania metody z określonymi parametrami:

struct NoDouble
{
  void f(int i);
  void f(double) = delete;
};




Ułatwienie używania wątków
W C++11 sam język dostarcza nowy model pamięci obsługujący wątki, ale w głównej mierze umożliwienie wielowątkowego pisania programów będzie zasługą biblioteki standardowej C++11.

Została dodana klasa wątku, która pobiera jako argument obiekt funkcyjny do uruchomienia w nowym wątku. Możliwe jest zamrażanie wątku, dopóki inny wątek nie zakończy swego działania, czyli dołączanie wątku (ang. thread joining). Dostęp do operacji specyficznych dla danej platformy jest możliwy wszędzie tam, gdzie jest to osiągalne.

Dla synchronizacji pomiędzy wątkami do biblioteki zostały dodane też muteksy i monitory.
W zastosowaniach wysokowydajnościowych i jednocześnie niskopoziomowych konieczna jest czasem komunikacja pomiędzy wątkami bez narzutu związanego z użyciem muteksów. Jest to osiągalne za pomocą operacji atomowych na lokacjach pamięci razem z odpowiednimi barierami pamięci. Biblioteka operacji atomowych pozwala na określanie minimalnej synchronizacji koniecznej dla jakiejś operacji.


Szablony ze zmienną listą parametrów
Szablony klas lub funkcji w poprzednich standardach C++, pobierały tylko określoną sekwencję argumentów. C++11 pozwala szablonom na pobieranie dowolnej liczby argumentów dowolnego typu.
template< typename... Values> class krotka;

Klasa szablonowa krotka będzie pobierać dowolną ilość nazw typów jako parametry szablonu:
np:
class krotka<std::vector<int>, std::map<std::string, std::vector<int> > > jakasNazwaInstancji;

Liczba argumentów może być zerowa, więc class krotka<> jakasNazwaInstancji także będzie działać.

Zmienne szablony (szablony o zmiennej liczbie argumentów) mogą także być zastosowane do funkcji, wprowadzając do nich bezpieczny typowo mechanizm podobny do zmiennej listy argumentów w C:

template< typename... Params> void printf(const std::string &strFormat, Params... parameters);


std::atomic dla współbieżności, volatile dla pamieci specjalnej

Po skonstruowaniu obiektu std::atomic operacje na nim zachowują
się, jakby były wewnątrz sekcji krytycznej chronionej przez muteks, ale operacje
te są zasadniczo implementowane przy użyciu specjalnych instrukcji maszynowych,

które są bardziej wydajne niż w przypadku użycia muteksu.
Rozważmy kod z użyciem std::atomic:

std::atomic<int> ai(0);                           // inicjowanie ai wartością 0
ai = 10;                                                  // atomowe ustawienie ai wartości 10
std::cout << ai;                                     // atomowe odczytanie wartości ai
++ai;                                                    // atomowe zwiększenie ai do 11
--ai;                                                      // atomowe zmniejszenie ai do 10

Podczas wykonywania tych poleceń inne wątki czytające zmienną ai mogą widzieć
tylko wartości 0, 10 lub 11. Żadne inne wartości nie są możliwe. Poleceniu „std::cout
<< ai;” fakt, że zmienna ai ma typ std::atomic, gwarantuje tylko, że odczyt ai jest atomowy. Nie ma gwarancji, że całe polecenie zostanie przetworzone atomowo.

działanie dwóch ostatnich poleceń – zwiększenia i zmniejszenia wartości ai. Każda z nich jest operacją odczyt-modyfi kacja-zapis (read-modify-write – RMW), jednak są one wykonywane atomowo. Jest to jedna z najprzyjemniejszych charakterystyk typów std::atomic:,
gdy obiekt std::atomic został raz skonstruowany, wszystkie jego funkcje składowe, w tym obejmujące operacje RMW, mają gwarancję, że będą widziane przez inne wątki jako atomowe.
W przeciwieństwie odpowiadający kod używający volatile nie ma prawie żadnych

gwarancji w kontekście wielowątkowym:

volatile int vi(0);                                  // inicjowanie vi wartością 0


Podczas wykonywania tego kodu, jeśli inne wątki odczytują wartość vi, mogą zobaczyć
cokolwiek, np. -12, 68, 4090727 – cokolwiek! Taki kod miałby niezdefi niowane
działanie, ponieważ te polecenia modyfi kują vi, więc jeśli inne wątki odczytują vi
w tym samym czasie, istnieją równoczesne odczyty i zapisy w pamięci, która nie
jest ani zadeklarowana jako std::atomic, ani chroniona przez muteks. Taka jest
właśnie defi nicja wyścigu danych.


C++ 14

C ++ 11 zezwala na dedukcję typu zwracanego funkcji lambda. C ++ 14 zapewnia tę zdolność do wszystkich funkcji

C ++ 14 dodaje składnię decltype (auto). Pozwala to na użycie reguł decltype dla wyrażenia auto.

Funkcje constexpr w C ++ 11 mogły zawierać tylko jedno zwracane wyrażenie.  C ++ 14 rozluźnia te ograniczenia.

C++ 11 Lista inicjująca nie mogła inicjalizować memberów które były inicjalizowane w konstruktorze, C ++14 potrafi.

Numeric literals in C++14 can be specified in binary form.[3] The syntax uses the prefixes 0b or 0B.


W C ++ 11 parametry funkcji lambda muszą być zadeklarowane dla konkretnych typów. C ++ 14 rozluźnia to wymaganie, umożliwiając zadeklarowanie parametrów funkcji lambda za pomocą specyfikatora typu auto.

Lambda capture expressions

C++11 lambda functions capture variables declared in their outer scope by value-copy or by reference. This means that value members of a lambda cannot be move-only types.[13] C++14 allows captured members to be initialized

std::unique_ptr<int> ptr(new int(10));
auto lambda = [value = std::move(ptr)] {return *value;};


Link do artykułu o Modernizacji kodu nowoczesnym c++ z Programisty:



C++ 17
Karta Features of c++17


Blog c++ i książka o c++17

Handy C++17 Features

Więcej Patrz rozdział o C++17/20



C vs C++

Nie pisz kodu C++ z wykorzystaniem technik z C, oto przykład niepoprawnego kodu:

Przykład kodu z stylu C:
#include <cstring>
#include <stdio.h>
#define ARRAYSIZE 5

int addition (int a, int b)
{ return (a+b); }

int subtraction (int a, int b)
{ return (a-b); }

typedef int (*operation)(int a, int b);

int main()
{

    char* animals [ARRAYSIZE] = {"zebra","horse","cow","elephant","lion"};
 int totalsize = 0;
 for (int i=0;i<ARRAYSIZE;i++)
 {
  totalsize += strlen(animals[i]) + 1; // allow for the space
 }
 totalsize++ ;  // allow for the null terminator
 char* allanimals = new char[totalsize];
 allanimals[0]='\0'; //put null terminator in place
 for (int i=0;i<ARRAYSIZE;i++)
 {
  strcat(allanimals,animals[i]);
  strcat(allanimals," ");
 }
    printf("All Animals: %s \n",allanimals);

    int numbers[ARRAYSIZE]={200,600,123,456,789};
    printf("Integer array \n");

    //use for loop to display int array elements
    for(int n=0;n<ARRAYSIZE;n++)
    {
        printf("Number %d: %d \n",n+1,numbers[n]);
    }
    printf("\n\n");

    printf("String array \n");

    //use for loop to display String array elements
    for(int m=0;m<ARRAYSIZE;m++)
    {
        printf("Animal %d: %s \n",m+1,animals[m]);
    }
    printf("\n\n");

    //use for loop to total int array elements
 int total=0;
 operation op =addition;
    for(int n=0;n<ARRAYSIZE;n++)
    {
        total=op(total, numbers[n]);
    }

 printf("Array total: %d \n",total);
    printf("\n\n");

 op = subtraction;
 total = op(total,numbers[ARRAYSIZE-1]);
 printf("Array total without last element: %d \n",total);
    printf("\n\n");

 return 0;
}


Przykład poprawnego kodu z wykorzystaniem C++
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;

constexpr int ARRAYSIZE= 5;

int main()
{

    vector<string> animals {"zebra","horse","cow","elephant","lion"};

 string allanimals = std::accumulate(begin(animals), end(animals),
  string("All Animals: "),[](string a, string b){return a+ " " + b;});
    cout << allanimals << endl;

 vector<double> numbers;
 numbers.reserve(ARRAYSIZE);
        numbers.push_back(200);
 numbers.push_back(600);
 numbers.push_back(123);
 numbers.push_back(456);
 numbers.push_back(789);
 numbers.push_back(89);
    cout << "Integer array" << endl;

 int n=0;
    std::for_each(begin(numbers),end(numbers),[&n](int elem)
    {
        cout << "Number " << ++n << ": " << elem << endl;
    });
    cout << endl << endl;

    cout << "String array" << endl;
    int m=0;
 std::for_each(begin(animals), end(animals), [&m](string elem)
    {
        cout << "Animal " << ++m << ": " << elem << endl;
    });
    cout << endl << endl;

 int total = std::accumulate(begin(numbers),end(numbers),0);
 int total2 = std::accumulate(begin(numbers),end(numbers),0,
  [](int a, int b){return a + (b/2);});

 cout << "Array total: " << total<< endl;
 cout << "Array half total: " << total2 << endl;
    cout << endl << endl;

 auto op = [] (int a, int b){ return (a-b); };

 total = op(total,numbers[numbers.size()-1]);
 cout << "Array total without last element: "<< total<< endl;
    cout << endl << endl;

 return 0;
}

- zamiast char*                              stosuj  std::string
- zamiast printf                              stosuj std::cout
- zamiast tablic[]                           stosuj kontenery np: std::vector
- zamiast #define                           stosuj zmienne stało wyrażeniowe constexpr (obliczenia na etapie kompilacji)
- zamiast makr                                    stosuj funkcje albo lambdy
- zamiast pętli z kodem                 stosuj algorytmy z lambdami
- zamiast wskaźników na funkcje stosuj lambdy (bądź virtualne funkcje przy dziedziczeniu, albo template)
- zamiast deklaracji typów             stosuj auto
- zamiast programowania proceduralnego stosuj programowanie obiektowe
- staraj się nie zarządzać pamięcią ręcznie (smart pointers, make zamiast new, kontenery std, jak możesz alokuj na stosie)
-używaj semantyki przenoszenia r-value (ale nie wszędzie RVO copy-elision)
-stosuj regułę zera

Powody:
-char*: trudne zarządzanie pamięcią, podatność na błędy
-printf: podatnośc na błędy przy rozpoznawaniu typów, przestarzały, niebezpieczny
-tablice[]: trudne zarządzanie pamięcią, wielkością, niebezpieczne
-makrodef: przestarzałe, podatne na błędy, nie zawierają typu ani zakresu
- pętle z kodem a algorytmy: algorytmy szybsze, bezpieczniejsze, czytelniejsze
- wskaźniku na funkcje: w C++ nie ma żadnego powodu aby ich używać !!!
- używanie auto: w przypadku zmiany typu głównego vectora, nie trzeba zmieniać typu w każdym miejscu, sam rozpozna
- użycie obiektowości: C++ jest obiektowy, korzystaj z tego (SOLID - założenia programowania obiektowego) RAII

C++ Advanced Topics pluralsight Link




64 sposoby na crash 
Link




Zwracanie wielu wartości: 

Zwracanie wielu wartości, link



Multithread:



uruchomienie funkcji:
#include <thread>

void my_thread_func()
{
    std::cout << "My first thread..." << std::endl;
}

int main()
{
    std::thread t(&my_thread_func);
    t.join();
}


uruchomienie metody obiektu:

std::thread th(&Student::pracuj, mojstudent);           


Student - klasa
pracuj   - metoda
mojstudent - obiekt


kompilowanie:      g++ -std=c++11 -pthread  file_name.cpp
                              g++  file_name.cpp  -o  file_name  -std=c++11  -pthread



można pominąc zmienna th i przekazywac id wątku np do tablicy

#include<iostream>
#include<stdlib.h>
#include <thread>

using namespace std;


void wyswietl()
{
  while(1)
  {
    cout<<"c++11 Thread wyswietlam swoj pid="<<pthread_self()<<endl;
//std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }
}

int main()
{


thread  myThreads[1000];                          
int i=0;

while(i<1000)
{    
    //std::thread{wyswietl}.detach();
    myThreads[i]=std::thread(wyswietl);
    i++;
}

int b;
cin>>b;  //nie koncz main

return 0;
}

Jeśli nie chcemy przekazywać ID a chcemy by każdy wątek był detach

std::thread{wyswietl}.detach();



Funkcja z argumentem
void task1(std::string msg)
{
    std::cout << "task1 says: " << msg;
}
wywołanie funkcji i podanie jej argumentu
std::thread t1(task1, "Hello");


                                                      Propozycje zmian w języku C++

  • const reference by default, deep copy only done explicitely (maybe `var` and `val` keywords from Kotlin instead of single `auto` keyword are good too);
  • less things done implicitly (eg. no implicit class member functions, like operator=, copy constructor, ... should be created);
  • mandatory override keyword;
  • Safe RAiI primitives instead of unsafe `new` and `delete` operators;
  • Some cleaner way to introduce UB;
  • Static interfaces for template parameters (template <typename T implements Comparable> )(if concepts from C++20 are too complicated, maybe they aren't);
  • Remove #define preprocessor macro, static reflections from C++23 are probably the last necessary feature, that makes it useless;




Developing in WSL

install WSL

Windows Terminal (Preview)

WSL2 link


CodeBlocks C++17


Zainstaluj CodeBlocks  LINK
Zainstaluj MinGW (8.2.0) LINK
strona MinGW link

-instalacja w c: tak by rozpakował w C:\MinGW
-dodaj zmienna środowiśkową PATCH dla C:\MinGW\bin

W CodeBlocks wejdz na settings/Compiler "zakladka toolChain executables" i ustaw ścieżkę na C:\MinGW
poniżej w Program files wybierz:
dla C         :    gcc.exe   (z C:\MinGW\bin)
dla C++     :   g++.exe
Linker for dynamics  :   g++.exe
Linker for static         :   ar.exe
Resource compiler     :    windres.exe
Make program           :     mingw32-make.exe
OK

jeszcze raz wejdz na settings/Compiler i ustaw ptaszka w CompilerSettings/Compiler Flags:
"Have g++ follow the coming C++1z(aka C++17) ISO C++ language standard [-std=C++1z] "


kod testujący C++17

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
 const int i=90;
if constexpr (i) //'if constexpr' is part of C++17
{ cout<< "True!"; }
else
{ cout<<"False" ; }
cin.get();
return 0;
}





Impl maszyny stanów w c++ oparta na std::variant. 

https://www.bfilipek.com/2019/06/fsm-variant-game.html


CppCoreGuidelines

Wewnątrz C++ ukrywa się mniejszy, prostszy, bezpieczniejszy język, który próbuje
wydostać się na zewnątrz – takimi słowami Bjarne podsumował ideę
stworzenia C++ Core Guidelines: uporządkowanego zbioru wskazówek dotyczących
stosowania języka C++ w sposób poprawny, bezpieczny i wydajny.
Przyjrzyjmy się bliżej zestawowi C++ Core Guidelines (C++CG), poczynając od
fundamentalnych założeń projektowych. Są one następujące:
-reguły zawarte w ramach C++CG zakładają korzystanie z aktualnej wersji
standardu języka C++ –  – oraz kompatybilność z aktualnymi
wersjami kompilatorów C++,
- poza samymi regułami C++CG oferuje kompaktową bibliotekę wspomagającą:
GSL (skrót od Guidelines Support Library), oraz zestaw narzędzi
do statycznej analizy kodu,
-- C++CG są skonstruowane w taki sposób, że nie wymagają poświęceń
w zakresie wydajności, prostoty oraz przenośności kodu,
-- C++CG to inicjatywa organiczna i społecznościowa; co przekłada się na
otwarty jej charakter; to projekt, który ma pomóc społeczności języka
C++ pisać nowoczesny, efektywny kod już teraz, a nie za kilka lat,
-- pomysłodawcy projektu otwarcie przyznają się, iż nie są w stanie sami
wykonać całej pracy potrzebnej do opracowania pełnego zestawu reguł
i zapraszają do współpracy całą społeczność języka C++.
Odnośnie samego katalogu zasad i wskazówek tworzonych w ramach C++CG
docelowo ma on być:
-- wszechstronny,
-- łatwo dostępny oraz prosty do przeglądania,
-- otwarty na integrację z szeregiem narzędzi (również komercyjnych),
-- umożliwiający stopniową adaptację,
-- normatywny,
-- łatwy do stosowania w edukacji (oferujący przesłanki oraz przykłady),
-- elastyczny (nadający się do stosowania w wielu dziedzinach programowania),
-- niewłasnościowy (aczkolwiek zbudowany w sposób profesjonalny
i odpowiedzialny).
Core Guidelines LINK



Big endian vs Little endian

W sytuacjach, kiedy liczby całkowite lub jakiekolwiek inne dane zapisywane są przy użyciu wielu (przynajmniej dwóch) bajtów, nie istnieje jeden unikatowy sposób uporządkowania tych bajtów w pamięci lub w czasie transmisji przez dowolne medium i musi być użyta jedna z wielu konwencji ustalająca kolejność bajtów (ang. byte order lub endianness). Jest to analogiczne do zapisu pozycyjnego liczb lub kierunku pisma w różnych językach – ze strony lewej na prawą albo z prawej na lewą.

Big endian[1] (spotykane także grubokońcowość) to forma zapisu danych, w której najbardziej znaczący bajt (zwany też górnym bajtem, z ang. high-order byte) umieszczony jest jako pierwszy[1].
Procesory, które używają formy big endian, to między innymi HP Intel ItaniumSPARCMotorola 68000[2]PowerPC 970IBM System/360, Siemens SIMATIC S7.
Jest ona analogiczna do używanego na co dzień przez ludzi sposobu zapisu liczb.
Procesor zapisujący 32-bitowe wartości w pamięci, przykładowo 0x4A3B2C1D pod adresem 100, umieszcza dane, zajmując adresy od 100 do 103 w następującej kolejności:
100101102103
...4A3B2C1D...
Przykładowe formaty plików, które zawierają dane w formacie big-endian[3]:

Little endian[edytuj | edytuj kod]

Little endian[1] (spotykane także cienkokońcowość) to forma zapisu danych, w której najmniej znaczący bajt (zwany też dolnym bajtem, z ang. low-order byte) umieszczony jest jako pierwszy[1]. Procesory, które używają formy little endian, to między innymi wszystkie z rodziny x86, DEC VAX.
Jest ona odwrotna do używanego na co dzień sposobu zapisu liczb.
Procesor zapisujący 32-bitowe wartości w pamięci, przykładowo 0x4A3B2C1D pod adresem 100, umieszcza dane zajmując adresy od 100 do 103 w następującej kolejności:
100101102103
...1D2C3B4A...
Przykładowe formaty plików, które zawierają dane w formacie little-endian[3]:


ARM - zmienna (BE albo LE )


Big endian's advantage:
  • Easier for (most) human to read when examining memory values. This sometimes also applies to serializing/deserializing values when communicating with networks.
  • Easier sign checking (by checking the byte at offset 0)
  • Easier comparison: useful in arbitrary-precision math, as numbers are compared from the most significant digit. But this is less important, because it’s not a common operation
  • No need for endianness conversion when sending/receiving data to/from the network. This is less useful because network adapters can already swap bytes and copy them to memory in the correct order without the help of the CPU, and most modern CPUs have the ability to swap bytes themselves
Little endian's advantage:
  • Easier parity checking (by checking the byte at offset 0 we can see that it’s odd or even)
  • Easier for some people to read: Arabic, Hebrew and many other languages write from right to left so they read numbers in little-endian order. Some languages also read number values in little-endian order (like 134 as 4 units, 3 tens and 1 hundred), so it’s easier to know how big the current digit is. That means the thousand separator is less useful to them, as we immediately know how big the current digit is
  • Natural in computation
    • Mathematics operations mostly work from least to most significant digit, so it's much easier to work in little-endian
    • This is extremely useful in Arbitrary-precision arithmetic (or any operations that are longer than the architecture's natural word size like doing 64-bit maths on 32-bit computers) because it would be much more painful to read the digits backwards and do operations
    • It’s also useful in situations like in case a computer with limited memory bandwidth (like some 32-bit ARM microcontrollers with 16-bit bus, or the Intel 8088 with 16-bit register but 8-bit data bus). Now the 32-bit CPU can do math 16 bits at a time by reading a halfword at address A, add it while still reading the remaining halfword at A+2 then do the final add instead of waiting for the two reads to be finished then adding from the LSB
  • Always reads as the same value if reading in the size less than or equal to the written value.
    • For example 20 = 0x14 if writing as a 64-bit value into memory at address A will be 14 00 00 00 00 00 00 00, and will always be read as 20 regardless of using 8, 16, 32, 64-bit reads (or actually any reads with length <= 64 at the address A like 24, 48 or 40 bits). This can be extended to arbitrarily longer types.
      • In big-endian system you have to know in which size you have written the value, in order to read it correctly. For example to get the least significant byte you need to read at byte A+n-1 (with n is the length in bytes of the write) instead of A.
    • This property also makes it easy to cast the value to a smaller type like int32_t to int16_t because the int16_t value will always lie at the beginning of int32_t.
In fact the designers of RISC-V architecture also said that:
We chose little-endian byte ordering for the RISC-V memory system because little-endian systems are currently dominant commercially (all x86 systems; iOS, Android, and Windows for ARM). A minor point is that we have also found little-endian memory systems to be more natural for hardware designers. However, certain application areas, such as IP networking, operate on big-endian data structures, and so we leave open the possibility of non-standard big-endian or bi-endian systems.

For background let's first examine a 32-word in both formats
Little Endian
Byte 3 Byte 2 Byte 1 Byte 0

In memory assuming storage at location 0xe0
0xe0   byte 3  0xef
oxe1   byte 2   oxeb
oxe2   byte 1   oxad
0xe3   byte 0   oxde

As seen in a hexdump:
0xefebadde

Big Endian
Byte 0 Byte 1 Byte 2 Byte 3

In memory assuming storage at location 0xe0
0xe0   byte 3  0xde
oxe1   byte 2   oxad
oxe2   byte 1   oxbe
0xe3   byte 0   oxef

As seen in a hexdump:
0xdeadbeef

Now as two 16-bit words
Little
0xadde  0xefbe

Big
oxdead 0xbeef

Advantages to little endian:
  • In assembly moving a value from location to another is exactly the same regardless of the word size. Position 0 goes to 0, 1 to 1, 2 to 2, etc.
  • If you add multiple bytes you start at the least significant byte and use the carry through the most significant byte. We're talking about the assembly to do this, not your higher level language that is using assembly as written by someone else.
  • Growing a little endian value in size means adding a byte at the end, whereas with big endian you must move all the bytes one to the right and add the new byte as the beginning.  This makes writing multiple precision math routines very easy to write. Same can be said for shrinking.
  • Smaller words can be written on non-word address boundaries, though not a hard fast rule it is generally easier for a programmer but harder for a hardware designer


Advantages to big endian:
  • It is easier for programmers to see values in memory as they are in "human order" for those that read left-to-right
  • You can check the sign of a number by looking at the first byte without even knowing how big the number is, whereas with little endian you must fetch them all or do offset math to find the sign and to do so you must know how large the word is
  • Most bitmapped graphics use MSB on the left as do network byte-orders and this makes big endian easier for both.
  • When decoding LZW compressed data (a very early form of data compression still very much used today) you can use the code word as an index into a look up table since it is MSB to LSB


In-class Member Initialisation: From C++11 to C++20

https://www.bfilipek.com/2015/02/non-static-data-members-initialization.html



Linki


LINK do notatek C++11-14

LINK do bloga o prog. wielowątkowym (pl)

LINK do bloga o prog. wielowątkowym (ang)

LINK do tutorial prog. wielowątkowe (ang)

Bartek's Filipek coding blog

C++17 In Detail Bartek's Filipek book

C++ User Group Krakow

Visual Studio Code - sposoby na wydajniejszą pracę

Cpp-polska

Krzaq blog

Notatki Grz

Build 8bit comp

24 interview-questions

Jetbrains (Clion IDE & Resharper-cpp ) 

       ReSharper C++ Feature Overview youtube

       Clion

       ReSharper

Nowoczesny C++ technologie prezentacja

Modern C++ ecosystem by Filipek 

CRTP - The Curiously Recurring Template Pattern
     
Kursy i zadania c++ online

Wolne jądro linux do 5G?

Why C++ Member Function Pointers Are 16 Bytes Wide

ULM prog

Krzysztof Ostrowski writing on:   https://isocpp.org/ 
own:  http://insooth.github.io/

https://www.reddit.com/r/cpp/top/?t=all

https://www.youtube.com/user/CppCon

wyłączenie przestrzeni do hibernacji:
powercfg -h off

https://adventofcode.com/

OPERACJE BITOWE:

HexEditorNeo  (export to C++ array)
HxD(hex&disk editor)
instrukcjaHxD

How does C++ linkage differ from C linkage?


MAGAZYN prog- PagedOut od GynvaelColdwind

An Open-Source Collection of +200 Algorithmic

WHEN TO USE CONST VS CONSTEXPR IN C++