CPP2019SSF20

Aus Verteilte Systeme - Wiki
Zur Navigation springen Zur Suche springen

Feature: noexcept

  • Eingeführt in: C++11
  • Deprecated seit: -
  • Nicht mehr im Standard seit: -

Syntax

function() noexcept    // als Bezeichner

noexcept (expression)  // als Operator

Use Cases

Bezeichner

Wird das keyword noexcept als Bezeichner benutzt, kennzeichnet es ob Funktionen Exceptions erzeugen können.

Vorteile
  • der Compiler kann optimieren da er keine Ausnahmebehandlung erzeugen muss
  • jeder der die Funktion aufruft sieht das sie keine Ausnahmen erzeugen wird
  • während dem Programmieren ist sofort ersichtlich das die Funktion sicher ist

Operator

Bei der Verwendung als Operator, prüft noexcept() zur Compilezeit ob ein Ausdruck eine Exception werfen kann. Kann eine Exception erzeugt werden erhalten wir den Rückgabewert False , andernfalls den Wert True.

Vorherige Lösungsansätze

Ein vorangegangener Lösungsansatz war das Keyword throw(), dieses ist allerdings seit C++11 als veraltet gekennzeichnet und wird in C++20 komplett entfernt.

Motivation für die Einführung

Bei der Verwendung des vorherigen Lösungsansatzes mit dem keyword throw() folgt bei einer doch ausgelösten Exception undefiniertes Verhalten. Dies erfordert letztendlich einen Aufruf von std::unexcpected. Um eine festgelegte Verhaltensweise für diesen Fall zu definieren wurde das hier behandelte keyword noexcept eingeführt, das in diesem Fall immer std::terminate() aufruft.

Warnhinweise

Bei einer mit noexcept markierten Funktion wird häufig davon ausgegangen das der Fall das diese eine Exception erzeugen kann niemals eintritt, dies ist nicht der Fall.

Es wird std::terminate() aufgerufen was letztendlich dazu führt das std::abort() ausgeführt wird und das Programm vollständig abstürzt.

Code-Beispiele

Bezeichner

void may_throw_exc();
void never_throws_exc() noexcept;

Die Funktion "never_throws_exc()" wurde mit dem Keyword noexcept markiert, es wird also versprochen das diese Funktion niemals eine Exception verursachen wird, und falls doch uns diese nicht interessiert.

Dahingegen wird über das Verhalten der Funktion "may_throw_exc()" keine Angabe gemacht, sie muss keine Exception erzeugen kann es aber durchaus durch einen Aufruf einer anderen unsicheren Funktion.

Operator

int main () {
 cout << "Wird may_throw_exc() NIEMALS eine Exception werfen? " << noexcept(may_throw_exc()) << endl;

 cout << "Wird never_throws_exc() NIEMALS eine Exception werfen? " << noexcept(never_throws_exc()) << endl;
}

Ausgehend von den im ersten Codebeispiel erstellten Funktionen verwenden wir nun "noexcept" als Operator um Funktionen auf ihr Exception - spezifisches Verhalten zu prüfen.

Wir erhalten folgende Ausgabe:

Wird may_throw_exc() NIEMALS eine Exception werfen? 0   
Wird never_throws_exc() NIEMALS eine Exception werfen? 1

Für unsere mit noexcept markierte Funktion erhalten wir den Wert 1 (True) für die Funktion ohne genauere Verhaltensspezifikation den Wert 0 (False) sie kann Exceptions werfen.

Funktionstemplates

Im folgenden Beispiel wird die Verwendung von noexcept in Verbindung mit Funktionstemplates dargestellt.

Wir definieren eine Klasse Noexceptarray die Zahlen in einem einfachen Array speichert und eine zweite Klasse Noexceptvector die dazu std::vector benutzt.

class NoexceptArray{
public:
  std::array<int, 5> arr{1, 2, 3, 4, 5};            
};

class NoexceptVector{
public:
  std::vector<int> v{1, 2, 3, 4 , 5};                
};

Um zu prüfen ob der Copy - Konstruktor der beiden Klassen Exceptions erzeugen kann legen wir ein Funktionstemplate an.

Zu beachten ist hier insbesondere der Code Abschnitt noexcept(noexcept(T(src))), hier steht das erste noexcept für den Funktionsspezifier und das zweite fungiert als Operator.

template <typename T> 
T copy(T const& src) noexcept(noexcept(T(src))){   
  return src; 
}

In unserer Main Methode legen wir jeweils ein Objekt an und prüfen ob ein Funktionsaufruf Exceptions erzeugen kann.

int main(){
    
    NoexceptArray noexceptarray;
    NoexceptVector noexceptvector;
        
    std::cout << "Ausgabe noexceptarray: " <<            // (4)
                  noexcept(copy(noexceptarray)) << std::endl;
                   
    std::cout << "Ausgabe noexceptvector: " <<         // (5)
                  noexcept(copy(noexceptvector)) << std::endl;

}

Das ausführen des Codes ergibt folgende Ausgabe, demnach kann bei der Verwendung in einem Vector der Copy Konstruktor fehlschlagen, die Klasse ist daher nicht "noexcept" also eine 0.

Ausgabe noexceptarray: 1                                                                                                                                                      
Ausgabe noexceptvector: 0

Quellen

http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2855.html#problem

https://docs.microsoft.com/de-de/cpp/cpp/exception-specifications-throw-cpp?view=vs-2019

https://www.heise.de/developer/artikel/C-Core-Guidelines-Der-noexcept-Spezifier-und-Operator-4121657.html

https://akrzemi1.wordpress.com/2014/04/24/noexcept-what-for/

https://en.cppreference.com/w/cpp/language/noexcept

https://www.nosid.org/cxx11-noexcept.html

"C++ Kurz und gut", Kylie Loudon / Rainer Grimm, O'Reilly Verlag, ISBN Print: 978-3-96009-078-6