C++11 (znany również jako C++0x) – trzecie wydanie standardu języka programowania C++ opublikowane we wrześniu 2011 r. i zastępujące poprzednią edycję standardu zwaną C++03 z 2003 r. W edycji C++11 wprowadzono kilka dodatków do rdzenia języka oraz znacznie rozszerzono bibliotekę standardową C++, m.in. o biblioteki zawarte w Raporcie Technicznym 1 z wyjątkiem biblioteki matematycznych funkcji specjalnych.

Podobnie jak w poprzednich wydaniach, język C++ jest standardem ISO/IEC opublikowanym jako kolejna edycja serii ISO/IEC 14882 pod nazwą „ISO/IEC 14882:2011”.

Od czasu wydania wersji C++11 opracowane zostały dwa kolejne standardy: C++14 (15 grudnia 2014) oraz C++17 (w grudniu 2017). Obecnie trwają prace nad standardem C++20.

Zmiany w nowym standardzie Jak wspomniano, modyfikacje C++ obejmują rdzeń języka oraz jego biblioteki standardowe. W trakcie rozwoju nowego standardu, Komitet zastosował następujące dyrektywy:

Utrzymywać stabilność i kompatybilność z C++98 i być może z C, Preferować wprowadzanie nowych możliwości przez rozszerzenie biblioteki standardowej zamiast rozszerzenia rdzenia języka, Preferować zmiany mogące rozwijać techniki programistyczne, Ulepszać C++ tak, aby ułatwić projektowanie systemów i bibliotek zamiast wprowadzać nowe możliwości, które mogłyby być przydatne tylko w szczególnych zastosowaniach, Zwiększać bezpieczeństwo typów poprzez wprowadzenie bezpieczniejszych zamienników aktualnych, mniej bezpiecznych technik, Zwiększać wydajność i zdolność bezpośredniej współpracy ze sprzętem, Dostarczyć odpowiednich rozwiązań dla rzeczywistych problemów praktycznych, Stosować zasadę "zero narzutu" (narzędzie może wymagać dodatkowego wsparcia tylko wtedy, gdy jest użyte), Uczynić C++ łatwiejszym do nauczania i uczenia się bez usuwania narzędzi potrzebnych ekspertom.

Rozszerzenia w rdzeniu C++ Obszary rdzenia C++, które zostały znacznie ulepszone, to między innymi: obsługa wielowątkowości, umożliwienie programowania generycznego, jednolite inicjowanie i rozszerzenia wydajnościowe. W niniejszym artykule główne cechy rdzenia C++ i zmiany w nim są pogrupowane w trzech sekcjach: rozszerzenia wydajnościowe, rozszerzenia związane z ułatwieniem stosowania C++ oraz nowe możliwości. Część cech mogłoby pasować do kilku grup naraz, ale zostały zaszeregowane tak, aby uwypuklić ich główne przeznaczenie.

Rozszerzenia wydajnościowe w rdzeniu języka Dotyczą cech języka poprawiających wydajność. Chodzi o szybkość lub zajmowaną pamięć.

Referencja do r-wartości/Semantyka przeniesienia Źródło W C++ obiekty tymczasowe (określane jako r-wartości, czyli wartości będące po prawej stronie operatora przypisania '='), mogą być przekazywane do funkcji, ale tylko jako referencje do stałej (const &). Z punktu widzenia takiej funkcji nie jest możliwe rozróżnienie pomiędzy aktualną r-wartością a zwykłym obiektem przekazanym jako const &. Ponieważ jest to typ const & nie jest także możliwa zmiana jego wartości.

C++11 wprowadza nowy typ referencyjny, zwany referencją do r-wartości, definiowany jako typename &&. Może być akceptowany jako nie-stała wartość, co pozwala obiektom na ich modyfikację. Taka zmiana umożliwia pewnym obiektom na stworzenie semantyki przenoszenia.

Przykładowo, std::vector jest wewnętrznie adapterem na zwykłą tablicę w stylu języka C, wraz z wielkością tej tablicy. Jeśli obiekt tymczasowy typu vector jest tworzony lub zwracany przez funkcję, to może być przechowany tylko przez stworzenie nowego obiektu vector wraz z kopiami wszystkich r-wartości. Jeśli obiekt tymczasowy jest niszczony, niszczone są też wszystkie dane. 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.

Niech na potrzeby niniejszego artykułu referencja do r-wartości nazywa się r-referencją. Analogicznie, niech zwykła referencja nazywa się l-referencją. Wtedy mając l-referencje i r-referencje, programista może pisać dokładne funkcje przekazujące (ang. forwarding functions). Dokładność polega tu na całkowitej zgodności typów. Gdy ma do dyspozycji tylko l-referencje (tak jest w starszym standardzie C++), wtedy ogólnie rzecz biorąc, takie funkcje miałyby tylko przybliżone listy argumentów (nie do końca zgodne typy). Gdy l-referencje i r-referencje stosuje się łącznie z funkcjami lub metodami szablonowymi o zmiennych listach argumentów (ang. variadic templates), wtedy uzyskuje się możliwość dokładnego przekazywania argumentów do innej funkcji o ustalonej liście parametrów. Jest to najbardziej przydatne przy przekazywaniu parametrów konstruktora w celu stworzenia takich funkcji fabrykujących (ang. factory functions), które automatycznie wywołają właściwy konstruktor dla tych argumentów.

Uogólnione wyrażenia stałe Źródło 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.

Zastosowanie constexpr do funkcji narzuca bardzo ścisłe ograniczenia na to, co funkcja może robić:

Funkcja musi posiadać typ zwracany różny od void, Zawartość funkcji musi być w postaci "return wyrażenie", wyrażenie musi być stałym wyrażeniem po zastąpieniu argumentu. To stałe wyrażenie może albo wywołać inne funkcje tylko wtedy, gdy te funkcje też są zadeklarowane ze słowem kluczowym constexpr albo używać inne stałe wyrażenia, Wszystkie formy rekursji w stałych wyrażeniach są zabronione, Funkcja zadeklarowana ze słowem kluczowym constexpr nie może być wywoływana, dopóki nie będzie zdefiniowana w swojej jednostce translacyjnej.

Zmienne także mogą być zdefiniowane jako stałowyrażeniowe:

constexpr double silaGrawitacji = 9.8; constexpr double grawitacjaKsiezyca = silaGrawitacji / 6;

Zmienne typu constexpr (stałowyrażeniowe) są niejawnie typu const. Mogą one przechować wyniki wyrażeń stałych lub stałowyrażeniowych konstruktorów (czyli zdefiniowanych ze słowem kluczowym constexpr).

W celu konstrukcji wartości stałowyrażeniowych z typów danych zdefiniowanych przez użytkownika, konstruktory muszą także być zadeklarowane jako constexpr. Taki stałowyrażeniowy konstruktor musi być zdefiniowany przed użyciem w jednostce translacyjnej; podobnie jest z metodami stałowyrażeniowymi. Muszą mieć puste ciała funkcji i muszą inicjować swoje składowe za pomocą stałych wyrażeń. Destruktory takich typów powinny być trywialne.

Typy użytkownika, gdy kopiują constexpr, także powinny być zdefiniowane jako constexpr w celu umożliwienia zwracania ich przez wartość przez stałowyrażeniową funkcję. Dowolna metoda klasy, np. konstruktor kopiujący, operatory przeciążone, itd., mogą być zadeklarowane jako constexpr dopóki odpowiadają one definicji stałowyrażeniowej metody. Umożliwia to kompilatorowi kopiowanie klas w czasie kompilacji, wykonywanie operacji na nich, itd.

Stałowyrażeniowa funkcja lub konstruktor, mogą być wywołane z niestałowyrażeniowymi argumentami. Jeśli stałowyrażeniowy literał całkowity może być przypisany do niestałowyrażeniowej zmiennej, to stałowyrażeniowa funkcja może być także wywołana z niestałowyrażeniowymi parametrami, a wynik jest przechowywany w zmiennej niestałowyrażeniowej. Słowo kluczowe constexpr tylko umożliwia stałość podczas kompilacji tylko wtedy, gdy wszystkie składowe wyrażenia są typu constexpr.

W standardzie C++20 do języka wprowadzono nowe słowo kluczowe – consteval. Funkcja oznaczona jako consteval, nazywana funkcją natychmiastową, zachowuje się tak samo jak funkcja constexpr, jednak może być wywoływana jedynie z argumentami constexpr. Wartość zwracana przez funkcję natychmiastową musi być zawsze znana podczas kompilacji.

Modyfikacja definicji tradycyjnych struktur danych (TSD) W starszym C++, struktura musi spełniać kilka wymagań, by stać się tradycyjną strukturą danych (TSD) (ang. Plain Old Data, POD). Jest kilka powodów, by chcieć, aby większa liczba typów danych spełniała te wymagania. Typy spełniające te wymagania pozwalają implementacjom na takie ułożenie składowych obiektów, które byłyby kompatybilne z C. Jednak lista reguł w C++03 jest przesadnie restrykcyjna.

C++11 rozluźnia kilka reguł dotyczących tworzenia obiektów TSD.

Klasa/struktura jest uważana za TSD, jeśli jest trywialna, standardowo ułożona i nie posiada żadnych niestatycznych składowych niebędących TSD-ami. Klasa/struktura jest trywialna, jeśli:

Posiada trywialny konstruktor domyślny. Może używać składni dla konstruktora domyślnego (JakisKonstruktor() = default;), Posiada trywialny konstruktor kopiujący, być może ze składnią domyślności, tj. ze słowem kluczowym default, Posiada trywialny operator przypisania, być może ze składnią domyślności, Posiada trywialny destruktor, który nie może być wirtualny.

Standardowo ułożona klasa/struktura to taka, która:

Posiada tylko niestatyczne pola, które są standardowo ułożone, Posiada ten sam poziom dostępu (private, protected, public) dla wszystkich niestatycznych składowych, Nie posiada wirtualnych metod, Nie posiada wirtualnych klas bazowych, Posiada tylko standardowo ułożone klasy bazowe, Nie posiada klas bazowych takiego samego typu jak pierwsze niestatyczne pole, Albo nie posiada klas bazowych z niestatycznymi składowymi, albo nie posiada niestatycznych pól w najbardziej pochodnej klasie i posiada co najwyżej jedną klasę bazową z niestatycznymi składowymi. W zasadzie, może być tylko jedna klasa z niestatycznymi składowymi w hierarchii klas.

Rozszerzenia w rdzeniu języka związane ze zwiększeniem wydajności kompilacji i łączenia Szablony zewnętrzne C++ musi stworzyć instancję szablonu zawsze kiedy napotka w pełni określony szablon w jednostce translacyjnej. Może to spowodować dramatyczny wzrost czasu kompilacji, szczególnie gdy instancja szablonu jest tworzona w wielu jednostkach translacyjnych przy użyciu tych samych parametrów. W starszym C++ nie jest bowiem możliwe wstrzymanie tworzenia instancji szablonu w takiej sytuacji.

C++11 wprowadza ideę szablonów zewnętrznych. Starszy C++ już posiada możliwość zmuszania kompilatora do tworzenia instancji w określonym miejscu.

template class std::vector<MojaKlasa>;