Namespaces
Variants

Function declaration

From de.cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Eine Funktionsdeklaration führt den Funktionsnamen und seinen Typ ein. Eine Funktionsdefinition verknüpft den Funktionsnamen/Typ mit dem Funktionskörper.

Inhaltsverzeichnis

Funktionsdeklaration

Funktionsdeklarationen können in jedem Gültigkeitsbereich erscheinen. Eine Funktionsdeklaration im Klassenbereich führt eine Klassenmitgliedsfunktion ein (sofern nicht das friend -Spezifizierer verwendet wird), siehe member functions und friend functions für Details.

noptr-declarator ( parameter-list ) cv  (optional) ref   (optional) except  (optional) attr  (optional) (1)
noptr-declarator ( parameter-list ) cv  (optional) ref   (optional) except  (optional) attr  (optional)
-> trailing
(2) (seit C++11)

(siehe Declarations für die anderen Formen der declarator Syntax)

1) Syntax für reguläre Funktionsdeklaratoren.
2) Deklaration von nachgestellten Rückgabetypen. Die decl-specifier-seq muss in diesem Fall das Schlüsselwort auto enthalten.
noptr-declarator - jeder gültige declarator , aber wenn er mit * , & oder && beginnt, muss er in Klammern eingeschlossen sein.
parameter-list - möglicherweise leere, kommagetrennte Liste der Funktionsparameter (siehe unten für Details)
attr - (seit C++11) eine Liste von Attributen . Diese Attribute werden auf den Typ der Funktion angewendet, nicht auf die Funktion selbst. Die Attribute für die Funktion erscheinen nach dem Bezeichner innerhalb des Deklarators und werden mit den Attributen kombiniert, die am Anfang der Deklaration erscheinen, falls vorhanden.
cv - const/volatile-Qualifizierung, nur zulässig in Nicht-statischen Memberfunktionsdeklarationen
ref - (seit C++11) Ref-Qualifizierung, nur zulässig in Nicht-statischen Memberfunktionsdeklarationen
except -

dynamische Ausnahmespezifikation

(bis C++11)

entweder dynamische Ausnahmespezifikation
oder noexcept-Spezifikation

(seit C++11)
(bis C++17)

noexcept-Spezifikation

(seit C++17)
trailing - Nachgestellter Rückgabetyp, nützlich wenn der Rückgabetyp von Argumentnamen abhängt, wie in template < class T, class U > auto add ( T t, U u ) - > decltype ( t + u ) ; oder kompliziert ist, wie in auto fpif ( int ) - > int ( * ) ( int )


Wie in Deklarationen erwähnt, kann der Deklarator von einer requires -Klausel gefolgt werden, die die zugehörigen Constraints für die Funktion deklariert. Diese müssen erfüllt sein, damit die Funktion durch Overload Resolution ausgewählt wird. (Beispiel: void f1 ( int a ) requires true ; ) Beachten Sie, dass das zugehörige Constraint Teil der Funktionssignatur, aber nicht Teil des Funktionstyps ist.

(seit C++20)

Funktionsdeklaratoren können mit anderen Deklaratoren gemischt werden, wo die Deklarationsspezifizierer-Sequenz es erlaubt:

// deklariert einen int, einen int*, eine Funktion und einen Zeiger auf eine Funktion
int a = 1, *p = NULL, f(), (*pf)(double);
// decl-specifier-seq ist int
// Deklarator f() deklariert (definiert aber nicht)
//                eine Funktion ohne Argumente, die int zurückgibt
struct S
{
    virtual int f(char) const, g(int) &&; // deklariert zwei nicht-statische Memberfunktionen
    virtual int f(char), x; // Kompilierfehler: virtual (in decl-specifier-seq)
                            // ist nur in Deklarationen von nicht-statischen
                            // Memberfunktionen erlaubt
};

Die Verwendung eines volatile-qualifizierten Objekttyps als Parametertyp oder Rückgabetyp ist veraltet.

(since C++20)

Der Rückgabetyp einer Funktion kann kein Funktionstyp oder Array-Typ sein (kann jedoch ein Zeiger oder eine Referenz auf diese sein).

Wie bei jeder Deklaration gelten sowohl Attribute, die vor der Deklaration erscheinen, als auch Attribute, die unmittelbar nach dem Bezeichner innerhalb des Deklarators erscheinen, für die deklarierte oder definierte Entität (in diesem Fall für die Funktion):

[[noreturn]] void f [[noreturn]] (); // OK: both attributes apply to the function f

Allerdings gelten die Attribute, die nach dem Deklarator erscheinen (in der obigen Syntax), für den Typ der Funktion, nicht für die Funktion selbst:

void f() [[noreturn]]; // Error: this attribute has no effect on the function itself
(seit C++11)

Rückgabetyp-Deduktion

Wenn die Deklarator-Spezifizierer-Sequenz der Funktionsdeklaration das Schlüsselwort auto enthält, kann der nachgestellte Rückgabetyp weggelassen werden und wird vom Compiler aus dem Typ des Operanden im nicht-verworfenen return -Statement abgeleitet. Wenn der Rückgabetyp nicht decltype ( auto ) verwendet, folgt die Ableitung den Regeln der Template-Argument-Ableitung :

int x = 1;
auto f() { return x; }        // Rückgabetyp ist int
const auto& f() { return x; } // Rückgabetyp ist const int&

Wenn der Rückgabetyp decltype ( auto ) ist, entspricht der Rückgabetyp dem, was erhalten würde, wenn der Operand in der return-Anweisung in decltype eingeschlossen wäre:

int x = 1;
decltype(auto) f() { return x; }  // Rückgabetyp ist int, gleich wie decltype(x)
decltype(auto) f() { return(x); } // Rückgabetyp ist int&, gleich wie decltype((x))

(Hinweis: „ const decltype ( auto ) & “ ist ein Fehler, decltype ( auto ) muss alleinstehend verwendet werden)

Wenn es mehrere return-Anweisungen gibt, müssen alle auf denselben Typ deduzieren:

auto f(bool val)
{
    if (val) return 123; // leitet Rückgabetyp int ab
    else return 3.14f;   // Fehler: leitet Rückgabetyp float ab
}

Wenn es keine return-Anweisung gibt oder wenn der Operand der return-Anweisung ein void-Ausdruck ist (einschließlich return-Anweisungen ohne Operand), muss der deklarierte Rückgabetyp entweder decltype ( auto ) sein, wobei der abgeleitete Rückgabetyp void ist, oder (möglicherweise cv-qualifiziertes) auto , wobei der abgeleitete Rückgabetyp dann (identisch cv-qualifiziertes) void ist:

auto f() {}              // gibt void zurück
auto g() { return f(); } // gibt void zurück
auto* x() {}             // Fehler: auto* kann nicht von void abgeleitet werden

Sobald eine return-Anweisung in einer Funktion gesehen wurde, kann der von dieser Anweisung abgeleitete Rückgabetyp im Rest der Funktion verwendet werden, einschließlich in anderen return-Anweisungen:

auto sum(int i)
{
    if (i == 1)
        return i;              // Rückgabetyp von sum ist int
    else
        return sum(i - 1) + i; // OK: Rückgabetyp von sum ist bereits bekannt
}

Wenn die return-Anweisung eine geschweifte Initialisierungsliste verwendet, ist Deduktion nicht erlaubt:

auto func() { return {1, 2, 3}; } // Fehler

Virtual functions und coroutines (seit C++20) können keine Rückgabetyp-Ableitung verwenden:

struct F
{
    virtual auto f() { return 2; } // Fehler
};

Function templates außer user-defined conversion functions können die Rückgabetyp-Ableitung verwenden. Die Ableitung erfolgt bei der Instanziierung, selbst wenn der Ausdruck in der return-Anweisung nicht dependent ist. Diese Instanziierung befindet sich nicht in einem immediate context für SFINAE .

template<class T>
auto f(T t) { return t; }
typedef decltype(f(1)) fint_t;    // instanziiert f<int> um den Rückgabetyp abzuleiten
template<class T>
auto f(T* t) { return *t; }
void g() { int (*p)(int*) = &f; } // instanziiert beide fs um Rückgabetypen zu bestimmen,
                                  // wählt die zweite Template-Überladung

Neudeklarationen oder Spezialisierungen von Funktionen oder Funktionsvorlagen, die Rückgabetypableitung verwenden, müssen dieselben Rückgabetyp-Platzhalter verwenden:

auto f(int num) { return num; }
// int f(int num);            // Fehler: kein Platzhalter-Rückgabetyp
// decltype(auto) f(int num); // Fehler: anderer Platzhalter
template<typename T>
auto g(T t) { return t; }
template auto g(int);     // OK: Rückgabetyp ist int
// template char g(char); // Fehler: keine Spezialisierung des Primär-Templates g

Ebenso dürfen Neudeklarationen oder Spezialisierungen von Funktionen oder Funktionsvorlagen, die keine Rückgabetypableitung verwenden, keinen Platzhalter verwenden:

int f(int num);
// auto f(int num) { return num; } // Fehler: keine Neudeklaration von f
template<typename T>
T g(T t) { return t; }
template int g(int);      // OK: Spezialisierung von T als int
// template auto g(char); // Fehler: keine Spezialisierung des primären Templates g

Explizite Instanziierungsdeklarationen instanziieren selbst keine Funktions-Templates, die Rückgabetypableitung verwenden:

template<typename T>
auto f(T t) { return t; }
extern template auto f(int); // instanziiert f<int> nicht
int (*p)(int) = f; // instanziiert f<int>, um dessen Rückgabetyp zu bestimmen,
                   // aber eine explizite Instanziierungsdefinition
                   // ist weiterhin irgendwo im Programm erforderlich
(seit C++14)

Parameterliste

Die Parameterliste bestimmt die Argumente, die beim Aufruf der Funktion angegeben werden können. Es handelt sich um eine durch Kommas getrennte Liste von Parameterdeklarationen , von denen jede die folgende Syntax hat:

attr  (optional) decl-specifier-seq declarator (1)

attr  (optional) this decl-specifier-seq declarator

(2) (seit C++23)
attr  (optional) decl-specifier-seq declarator = initializer (3)
attr  (optional) decl-specifier-seq abstract-declarator  (optional) (4)

attr  (optional) this decl-specifier-seq abstract-declarator  (optional)

(5) (seit C++23)
attr  (optional) decl-specifier-seq abstract-declarator  (optional) = initializer (6)
void (7)
1) Deklariert einen benannten (formalen) Parameter. Für die Bedeutungen von decl-specifier-seq und declarator , siehe declarations .
int f ( int a, int * p, int ( * ( * x ) ( double ) ) [ 3 ] ) ;
2) Deklariert einen benannten explicit object parameter .
3) Deklariert einen benannten (formalen) Parameter mit einem Standardwert .
int f ( int a = 7 , int * p = nullptr, int ( * ( * x ) ( double ) ) [ 3 ] = nullptr ) ;
4) Deklariert einen unbenannten Parameter.
int f ( int , int * , int ( * ( * ) ( double ) ) [ 3 ] ) ;
5) Deklariert einen unbenannten explicit object parameter .
6) Deklariert einen unbenannten Parameter mit einem Standardwert .
int f ( int = 7 , int * = nullptr, int ( * ( * ) ( double ) ) [ 3 ] = nullptr ) ;
7) Zeigt an, dass die Funktion keine Parameter akzeptiert; es ist das exakte Synonym für eine leere Parameterliste: int f ( void ) ; und int f ( ) ; deklarieren dieselbe Funktion.
void ist die einzige Syntax, die einer leeren Parameterliste entspricht. Andere Verwendungen von void -Parametern sind fehlerhaft:
Falsche Verwendung Beispiel
mehrere Parameter vorhanden sind int f1 ( void , int ) ;
der void -Parameter benannt ist inf f2 ( void param ) ;
void CV-qualifiziert ist int f3 ( const void ) ;
void ein abhängiger Name ist int f4 ( T ) ; (wobei T void ist)
der void -Parameter ein expliziter Objektparameter ist (seit C++23) int f5 ( this void ) ;

Obwohl decl-specifier-seq impliziert, dass es Spezifizierer außer Typspezifizierern geben kann, ist der einzige andere erlaubte Spezifizierer register sowie auto (bis C++11) , und er hat keine Wirkung.

(bis C++17)

Falls einer der Funktionsparameter einen Platzhalter (entweder auto oder einen Konzepttyp ) verwendet, ist die Funktionsdeklaration stattdessen eine Abgekürzte Funktions-Template-Deklaration :

void f1(auto);    // same as template<class T> void f1(T)
void f2(C1 auto); // same as template<C1 T> void f2(T), if C1 is a concept
(seit C++20)

Eine Parameterdeklaration mit dem Spezifizierer this (Syntax ( 2 ) / ( 5 ) ) deklariert einen Expliziten Objektparameter .

Ein expliziter Objektparameter kann kein Funktionsparameter-Pack sein und kann nur als erster Parameter der Parameterliste in den folgenden Deklarationen erscheinen:

Eine Elementfunktion mit einem expliziten Objektparameter unterliegt den folgenden Einschränkungen:

  • Die Funktion ist nicht static .
  • Die Funktion ist nicht virtual .
  • Der Deklarator der Funktion enthält keine cv - und ref -Qualifizierer.
struct C
{
    void f(this C& self);     // OK
    template<typename Self>
    void g(this Self&& self); // also OK for templates
    void p(this C) const;     // Error: “const” not allowed here
    static void q(this C);    // Error: “static” not allowed here
    void r(int, this C);      // Error: an explicit object parameter
                              //        can only be the first parameter
};
// void func(this C& self);   // Error: non-member functions cannot have
                              //        an explicit object parameter
(seit C++23)

Parameternamen, die in Funktionsdeklarationen deklariert werden, dienen in der Regel nur der Selbst-Dokumentation. Sie werden (bleiben aber optional) in Funktionsdefinitionen verwendet.

Eine Mehrdeutigkeit entsteht in einer Parameterliste, wenn ein Typname in Klammern geschachtelt ist (einschließlich Lambda-Ausdrücken ) (seit C++11) . In diesem Fall besteht die Wahl zwischen der Deklaration eines Parameters vom Typ Zeiger auf Funktion und der Deklaration eines Parameters mit redundanten Klammern um den Bezeichner des Deklarators . Die Auflösung besteht darin, den Typnamen als einen einfachen Typspezifizierer (welcher der Zeiger-auf-Funktion-Typ ist) zu betrachten:

class C {};
void f(int(C)) {} // void f(int(*fp)(C param)) {}
                  // NICHT void f(int C) {}
void g(int *(C[10])); // void g(int *(*fp)(C param[10]));
                      // NICHT void g(int *C[10]);
**Änderungen:** - "NOT" in den Kommentaren wurde zu "NICHT" übersetzt - Alle HTML-Tags, Attribute und Code-Blöcke wurden unverändert beibehalten - C++-spezifische Begriffe (class, void, int, etc.) wurden nicht übersetzt - Die Formatierung und Struktur des Originalcodes bleibt vollständig erhalten

Parametertyp darf kein Typ sein, der eine Referenz oder einen Zeiger auf ein Array unbekannter Größe enthält, einschließlich mehrstufiger Zeiger/Arrays solcher Typen oder eines Zeigers auf Funktionen, deren Parameter solche Typen sind.

Verwendung einer Ellipse

Der letzte Parameter in der Parameterliste kann eine Ellipse sein ( ... ); dies deklariert eine variadische Funktion . Das Komma vor der Ellipse kann weggelassen werden (in C++26 als veraltet markiert) :

int printf(const char* fmt, ...); // eine variadische Funktion
int printf(const char* fmt...);   // wie oben, aber seit C++26 veraltet
template<typename... Args>
void f(Args..., ...); // eine variadische Funktionsvorlage mit einem Parameterpaket
template<typename... Args>
void f(Args... ...);  // wie oben, aber seit C++26 veraltet
template<typename... Args>
void f(Args......);   // wie oben, aber seit C++26 veraltet

Funktionstyp

Parameter-Typen-Liste

Die parameter-type-list einer Funktion wird wie folgt bestimmt:

  1. Der Typ jedes Parameters (einschließlich Parameter Packs ) (seit C++11) wird aus seiner eigenen Parameterdeklaration bestimmt.
  2. Nach der Bestimmung des Typs jedes Parameters wird jeder Parameter vom Typ "Array von T " oder vom Funktionstyp T zu "Zeiger auf T " angepasst.
  3. Nach der Erstellung der Liste der Parametertypen werden alle CV-Qualifizierer auf oberster Ebene, die einen Parametertyp modifizieren, bei der Bildung des Funktionstyps gelöscht.
  4. Die resultierende Liste transformierter Parametertypen und das Vorhandensein oder Fehlen der Ellipse oder eines Parameter Packs (seit C++11) bilden die Parameter-Typen-Liste der Funktion.
void f(char*);         // #1
void f(char[]) {}      // definiert #1
void f(const char*) {} // OK, weitere Überladung
void f(char* const) {} // Fehler: definiert #1 neu
void g(char(*)[2]);   // #2
void g(char[3][2]) {} // definiert #2
void g(char[3][3]) {} // OK, weitere Überladung
void h(int x(const int)); // #3
void h(int (*)(int)) {}   // definiert #3

Bestimmung des Funktionstyps

In der Syntax (1) , unter der Annahme, dass noptr-declarator als eigenständige Deklaration betrachtet wird, wird der Typ der qualified-id oder unqualified-id in noptr-declarator als "derived-declarator-type-list T " angegeben:

  • Wenn die Ausnahmespezifikation nicht-werfend ist, lautet der Typ der deklarierten Funktion
    „abgeleiteter-Deklarator-Typen-Liste noexcept Funktion von
    Parameter-Typen-Liste cv  (optional) ref   (optional) zurückgebend T “.
(seit C++17)
  • Der (bis C++17) Andernfalls ist der (seit C++17) Typ der deklarierten Funktion
    „abgeleiteter-Deklarator-Typen-Liste Funktion von
    Parameter-Typen-Liste cv  (optional) ref   (optional) (seit C++11) zurückgebend T “.

In Syntax (2) , unter der Annahme, dass noptr-declarator als eigenständige Deklaration betrachtet wird, und mit dem Typ der qualified-id oder unqualified-id in noptr-declarator als "derived-declarator-type-list T " ( T muss in diesem Fall auto sein):

(seit C++11)
  • Wenn die Ausnahmespezifikation nicht-werfend ist, lautet der Typ der deklarierten Funktion
    "derived-declarator-type-list noexcept Funktion von
    parameter-type-list cv  (optional) ref   (optional) zurückgebend trailing ".
(seit C++17)
  • Der (bis C++17) Andernfalls lautet der (seit C++17) Typ der deklarierten Funktion
    "derived-declarator-type-list Funktion von
    parameter-type-list cv  (optional) ref   (optional) zurückgebend trailing ".

attr , falls vorhanden, gilt für den Funktionstyp.

(seit C++11)
// der Typ von "f1" ist
// "Funktion von int mit Rückgabe void, mit Attribut noreturn"
void f1(int a) [[noreturn]];
// der Typ von "f2" ist
// "constexpr noexcept Funktion von Zeiger auf int mit Rückgabe int"
constexpr auto f2(int[] b) noexcept -> int;
struct X
{
    // der Typ von "f3" ist
    // "Funktion ohne Parameter const mit Rückgabe const int"
    const int f3() const;
};

Nachgestellte Qualifizierer

Ein Funktionstyp mit cv  oder ref   (seit C++11) (einschließlich eines durch typedef benannten Typs) kann nur erscheinen als:

typedef int FIC(int) const;
FIC f;     // Fehler: deklariert keine Memberfunktion
struct S
{
    FIC f; // OK
};
FIC S::*pm = &S::f; // OK

Funktionssignatur

Jede Funktion hat eine Signatur.

Die Signatur einer Funktion besteht aus ihrem Namen und der Parameter-Type-List . Ihre Signatur enthält auch den umschließenden Namespace , mit folgenden Ausnahmen:

  • Wenn die Funktion eine Member-Funktion ist, enthält ihre Signatur die Klasse, zu der die Funktion gehört, anstelle des umschließenden Namensraums. Ihre Signatur enthält auch die folgenden Komponenten, falls vorhanden:
  • cv
  • ref
(seit C++11)
  • trailing requires clause
  • Wenn die Funktion eine Nicht-Template- friend -Funktion mit einer trailing requires -Klausel ist, enthält ihre Signatur die umschließende Klasse anstelle des umschließenden Namensraums. Die Signatur enthält ebenfalls die trailing requires -Klausel.
(seit C++20)

except und attr (seit C++11) betrifft nicht die Funktionssignatur , obwohl noexcept Spezifikation den Funktionstyp beeinflusst (seit C++17) .

Funktionsdefinition

Eine Nicht-Mitgliedsfunktionsdefinition darf nur im Namensbereich auftreten (es gibt keine geschachtelten Funktionen). Eine Mitgliedsfunktionsdefinition kann auch im Rumpf einer Klassendefinition erscheinen. Sie haben folgende Syntax:

attr  (optional) decl-specifier-seq  (optional) declarator
virt-specs  (optional) contract-specs  (optional) function-body
(1)
attr  (optional) decl-specifier-seq  (optional) declarator
requires-clause contract-specs  (optional) function-body
(2) (seit C++20)
1) Eine Funktionsdefinition ohne Einschränkungen.
2) Eine Funktionsdefinition mit Constraints.
attr - (seit C++11) eine Liste von Attributen . Diese Attribute werden mit den Attributen nach dem Bezeichner im Deklarator kombiniert (siehe oben auf dieser Seite), falls vorhanden.
decl-specifier-seq - der Rückgabetyp mit Spezifizierern, wie in der Deklarationsgrammatik
declarator - Funktionsdeklarator, identisch mit der Funktionsdeklarationsgrammatik oben (kann geklammert sein)
virt-specs - (seit C++11) override , final , oder deren Kombination in beliebiger Reihenfolge
requires-clause - eine requires -Klausel
contract-specs - (seit C++26) eine Liste von Funktionsvertragsspezifizierern
function-body - der Funktionsrumpf (siehe unten)


function-body ist eines der folgenden:

ctor-initializer  (optional) compound-statement (1)
function-try-block (2)
= default ; (3) (seit C++11)
= delete ; (4) (seit C++11)
= delete ( string-literal ); (5) (seit C++26)
1) Regulärer Funktionsrumpf.
3) Explizit definierte Standardfunktion.
4) Explizit gelöschte Funktionsdefinition.
5) Explizit gelöschte Funktionsdefinition mit Fehlermeldung.
ctor-initializer - Member-Initialisierungsliste , nur in Konstruktoren erlaubt
compound-statement - die geschweifte Klammern umschließende Anweisungssequenz , die den Rumpf einer Funktion bildet
function-try-block - ein Funktions- try Block
string-literal - ein nicht ausgewertetes String-Literal , das zur Erläuterung des Grundes verwendet werden kann, warum die Funktion gelöscht wurde
int max(int a, int b, int c)
{
    int m = (a > b) ? a : b;
    return (m > c) ? m : c;
}
// Deklarator-Spezifizierer-Sequenz ist "int"
// Deklarator ist "max(int a, int b, int c)"
// Rumpf ist { ... }

Der Funktionsrumpf ist eine compound statement (Folge von null oder mehr Anweisungen, umgeben von einem Paar geschweifter Klammern), die ausgeführt wird, wenn der Funktionsaufruf erfolgt. Darüber hinaus umfasst der Funktionsrumpf eines constructor auch Folgendes:

Wenn eine Funktionsdefinition einen virt-specs enthält, muss sie eine Memberfunktion definieren.

(since C++11)

Wenn eine Funktionsdefinition eine requires-clause enthält, muss sie eine templatierte Funktion definieren.

(since C++20)
void f() override {} // Fehler: keine Member-Funktion
void g() requires (sizeof(int) == 4) {} // Fehler: keine templatisierte Funktion

Die Parametertypen sowie der Rückgabetyp einer Funktionsdefinition dürfen keine (möglicherweise cv-qualifizierten) unvollständigen Klassentypen sein, es sei denn, die Funktion ist als deleted definiert (seit C++11) . Die Vollständigkeitsprüfung erfolgt nur im Funktionsrumpf, was es Memberfunktionen ermöglicht, die Klasse, in der sie definiert sind (oder ihre umschließende Klasse), zurückzugeben, selbst wenn diese zum Zeitpunkt der Definition unvollständig ist (im Funktionsrumpf ist sie vollständig).

Die in der Deklaration einer Funktionsdefinition deklarierten Parameter sind im Gültigkeitsbereich innerhalb des Funktionskörpers. Wenn ein Parameter im Funktionskörper nicht verwendet wird, muss er nicht benannt werden (es genügt, einen abstrakten Deklarator zu verwenden):

void print(int a, int) // zweiter Parameter wird nicht verwendet
{
    std::printf("a = %d\n", a);
}

Obwohl die obersten cv-Qualifizierer in Funktionsdeklarationen auf den Parametern verworfen werden, modifizieren sie den Typ des Parameters, wie er im Rumpf einer Funktion sichtbar ist:

void f(const int n) // deklariert Funktion vom Typ void(int)
{
    // aber im Körper ist der Typ von "n" const int
}

Defaultierte Funktionen

Wenn die Funktionsdefinition von der Syntax ( 3 ) ist, wird die Funktion als explizit defaulted definiert.

Eine explizit defaulted Funktion muss eine special member function oder comparison operator function (since C++20) sein und darf keine default argument haben.

Eine explizit default-spezifizierte spezielle Elementfunktion F1 darf sich von der entsprechenden speziellen Elementfunktion F2 unterscheiden, die implizit deklariert worden wäre, wie folgt:

  • F1 und F2 können unterschiedliche ref und/oder except haben.
  • Wenn F2 einen Nicht-Objekt-Parameter vom Typ const C & hat, kann der entsprechende Nicht-Objekt-Parameter von F1 vom Typ C& sein.
  • Falls F2 einen impliziten Objektparameter vom Typ "Referenz auf C " besitzt, kann F1 eine explizite Objekt-Memberfunktion sein, deren expliziter Objektparameter vom (möglicherweise unterschiedlichen) Typ "Referenz auf C " ist. In diesem Fall würde sich der Typ von F1 vom Typ von F2 dadurch unterscheiden, dass der Typ von F1 einen zusätzlichen Parameter besitzt.
(seit C++23)

Wenn sich der Typ von F1 vom Typ von F2 auf eine andere Weise unterscheidet, als durch die vorhergehenden Regeln erlaubt, dann:

  • Wenn F1 ein Zuweisungsoperator ist und der Rückgabetyp von F1 sich vom Rückgabetyp von F2 unterscheidet oder der Nicht-Objekt-Parametertyp von F1 kein Referenztyp ist, ist das Programm fehlerhaft.
  • Andernfalls, wenn F1 in seiner ersten Deklaration explizit als default deklariert ist, wird es als gelöscht definiert.
  • Andernfalls ist das Programm fehlerhaft.

Eine Funktion, die bei ihrer ersten Deklaration explizit als default deklariert wird, ist implizit inline und ist implizit constexpr, wenn sie eine constexpr-Funktion sein kann.

struct S
{
    S(int a = 0) = default;             // Fehler: Standardargument
    void operator=(const S&) = default; // Fehler: Nicht übereinstimmender Rückgabetyp
    ~S() noexcept(false) = default;     // OK, andere Ausnahmespezifikation
private:
    int i;
    S(S&);          // OK, privater Kopierkonstruktor
};
S::S(S&) = default; // OK, definiert Kopierkonstruktor

Explizit defaulted Funktionen und implizit deklarierte Funktionen werden zusammen als defaulted Funktionen bezeichnet. Ihre tatsächlichen Definitionen werden implizit bereitgestellt, siehe die entsprechenden Seiten für Details.

Gelöschte Funktionen

Wenn die Funktionsdefinition die Syntax ( 4 ) oder ( 5 ) (since C++26) hat, ist die Funktion als explizit gelöscht definiert.

Jede Verwendung einer gelöschten Funktion ist fehlerhaft (das Programm wird nicht kompilieren). Dies umfasst Aufrufe, sowohl explizite (mit einem Funktionsaufrufoperator) als auch implizite (ein Aufruf eines gelöschten überladenen Operators, einer speziellen Memberfunktion, einer Allokationsfunktion etc.), das Bilden eines Zeigers oder Zeigers-auf-Member auf eine gelöschte Funktion, und sogar die Verwendung einer gelöschten Funktion in einem Ausdruck, der nicht potenziell ausgewertet wird.

Eine nicht-reine virtuelle Memberfunktion kann als gelöscht definiert werden, auch wenn sie implizit ODR-used wird. Eine gelöschte Funktion kann nur durch gelöschte Funktionen überschrieben werden, und eine nicht-gelöschte Funktion kann nur durch nicht-gelöschte Funktionen überschrieben werden.

Falls string-literal vorhanden ist, wird die Implementierung ermutigt, dessen Text als Teil der resultierenden Diagnosemeldung einzubeziehen, welche die Begründung für die Löschung zeigt oder eine Alternative vorschlägt.

(since C++26)

Wenn die Funktion überladen ist, Overload Resolution findet zuerst statt, und das Programm ist nur dann fehlerhaft, wenn die gelöschte Funktion ausgewählt wurde:

struct T
{
    void* operator new(std::size_t) = delete;
    void* operator new[](std::size_t) = delete("new[] is deleted"); // seit C++26
};
T* p = new T;    // Fehler: Versuch, gelöschten T::operator new aufzurufen
T* p = new T[5]; // Fehler: Versuch, gelöschten T::operator new[] aufzurufen,
                 //        gibt eine Diagnosemeldung "new[] is deleted" aus

Die gelöschte Definition einer Funktion muss die erste Deklaration in einer Übersetzungseinheit sein: Eine zuvor deklarierte Funktion kann nicht als gelöscht neu deklariert werden:

struct T { T(); };
T::T() = delete; // Fehler: Muss bei der ersten Deklaration gelöscht werden

Vom Benutzer bereitgestellte Funktionen

Eine Funktion ist user-provided , wenn sie user-declared ist und nicht explizit defaulted oder deleted bei ihrer ersten Deklaration. Eine user-provided explicitly-defaulted Funktion (d.h. explizit defaulted nach ihrer ersten Deklaration) wird an dem Punkt definiert, an dem sie explizit defaulted wird; wenn eine solche Funktion implizit als deleted definiert wird, ist das Programm ill-formed. Das Deklarieren einer Funktion als defaulted nach ihrer ersten Deklaration kann effiziente Ausführung und prägnante Definition bieten, während sie eine stabile binäre Schnittstelle zu einer sich entwickelnden Codebasis ermöglicht.

// Alle speziellen Elementfunktionen von "trivial" sind
// jeweils in ihren ersten Deklarationen standardmäßig implementiert,
// sie sind nicht benutzerbereitgestellt
struct trivial
{
    trivial() = default;
    trivial(const trivial&) = default;
    trivial(trivial&&) = default;
    trivial& operator=(const trivial&) = default;
    trivial& operator=(trivial&&) = default;
    ~trivial() = default;
};
struct nontrivial
{
    nontrivial(); // erste Deklaration
};
// nicht in der ersten Deklaration standardmäßig implementiert,
// ist benutzerbereitgestellt und wird hier definiert
nontrivial::nontrivial() = default;

Auflösung von Mehrdeutigkeiten

Im Falle einer Mehrdeutigkeit zwischen einem Funktionsrumpf und einem Initialisierer , der mit { oder = (seit C++26) beginnt, wird die Mehrdeutigkeit durch Überprüfung des Typs des Deklarator-Identifiers von noptr-declarator  aufgelöst:

  • Wenn der Typ ein Funktionstyp ist, wird die mehrdeutige Token-Sequenz als Funktionsrumpf behandelt.
  • Andernfalls wird die mehrdeutige Token-Sequenz als Initialisierer behandelt.
using T = void(); // Funktions-Typ
using U = int;    // Nicht-Funktions-Typ
T a{}; // definiert eine Funktion, die nichts tut
U b{}; // wertinitialisiert ein int-Objekt
T c = delete("hello"); // definiert eine Funktion als gelöscht
U d = delete("hello"); // kopierinitialisiert ein int-Objekt mit
                       // dem Ergebnis eines delete-Ausdrucks (fehlerhaft)

__func__

Im Funktionsrumpf ist die funktionslokale vordefinierte Variable __func__ definiert, als ob durch

static const char __func__[] = "Funktionsname";

Diese Variable hat Blockgültigkeit und statische Speicherdauer:

struct S
{
    S(): s(__func__) {} // OK: Initialisierungsliste ist Teil des Funktionsrumpfs
    const char* s;
};
void f(const char* s = __func__); // Fehler: Parameterliste ist Teil des Deklarators
#include <iostream>
void Foo() { std::cout << __func__ << ' '; }
struct Bar
{
    Bar() { std::cout << __func__ << ' '; }
    ~Bar() { std::cout << __func__ << ' '; }
    struct Pub { Pub() { std::cout << __func__ << ' '; } };
};
int main()
{
    Foo();
    Bar bar;
    Bar::Pub pub;
}

Mögliche Ausgabe:

Foo Bar Pub ~Bar
(seit C++11)

Funktionsvertragsspezifizierer

Funktionsdeklarationen und Lambda-Ausdrücke können eine Sequenz von Funktionsvertragsspezifizierern  enthalten, jeder Spezifizierer hat folgende Syntax:

pre attr  (optional) ( predicate ) (1)
post attr  (optional) ( predicate ) (2)
post attr  (optional) ( identifier result-attr  (optional) : predicate ) (3)
**Anmerkung:** Da alle Textinhalte innerhalb von ``-Tags oder C++-spezifischen Begriffen (wie "pre", "post", "attr", "predicate", "identifier", "result-attr") liegen, wurde gemäß den Anforderungen keine Übersetzung vorgenommen. Die numerischen Bezeichnungen (1), (2), (3) bleiben ebenfalls unverändert, da sie Teil der technischen Dokumentationsstruktur sind.
1) Führt eine Vorbedingungsassertion ein.
2,3) Führt eine Postcondition-Assertion  ein.
2) Die Assertion bindet nicht an das Ergebnis.
3) Die Assertion bindet an das Ergebnis.
attr - eine Liste von Attributen, die zur eingeführten Vertragsassertion gehören
predicate - ein beliebiger Ausdruck (außer nicht in Klammern gesetzten Komma-Ausdrücken )
identifier - der Bezeichner, der sich auf das Ergebnis bezieht
result-attr - eine Liste von Attributen, die zur Ergebnisbindung gehören


Precondition assertion und Postcondition assertion werden zusammen als function contract assertion  bezeichnet.

Ein Funktionsvertragsassertion ist eine Vertragsassertion , die einer Funktion zugeordnet ist. Das Prädikat einer Funktionsvertragsassertion wird kontextuell konvertiert zu bool .

Die folgenden Funktionen können nicht mit Funktionsvertragsspezifizierern deklariert werden:

Vorbedingungs-Assertions

Eine Vorbedingungsassertion ist mit dem Eintritt in eine Funktion verbunden:

int divide(int dividend, int divisor) pre(divisor != 0)
{
    return dividend / divisor;
}
double square_root(double num) pre(num >= 0)
{
    return std::sqrt(num);
}

Nachbedingungs-Zusicherungen

Eine Nachbedingungsassertion ist mit dem normalen Verlassen einer Funktion verbunden.

Wenn eine Nachbedingungsassertion einen identifier hat, führt der Funktionsvertrag-Spezifizierer identifier als Namen einer result binding der zugehörigen Funktion ein. Eine result binding bezeichnet das Objekt oder die Referenz, die durch den Aufruf dieser Funktion zurückgegeben wird. Der Typ einer result binding ist der Rückgabetyp ihrer zugehörigen Funktion.

int absolute_value(int num) post(r : r >= 0)
{
    return std::abs(num);
}
double sine(double num) post(r : r >= -1.0 && r <= 1.0)
{
    if (std::isnan(num) || std::isinf(num))
        // Beenden über eine Exception verursacht niemals Vertragsverletzung
        throw std::invalid_argument("Ungültiges Argument");
    return std::sin(num);
}

Wenn eine Nachbedingungs-Assertion einen Bezeichner  besitzt und der Rückgabetyp der zugehörigen Funktion (möglicherweise cv-qualifiziert) void ist, ist das Programm fehlerhaft:

void f() post(r : r > 0); // Fehler: kein Wert kann an „r“ gebunden werden

Wenn der deklarierte Rückgabetyp einer nicht-templatisierten Funktion einen Platzhaltertyp enthält, kann eine Nachbedingungsassertion mit einem Bezeichner  nur in einer Funktionsdefinition erscheinen:

auto g(auto&) post(r : r >= 0); // OK, „g“ ist ein Template
auto h() post(r : r >= 0);      // Fehler: Rückgabewert kann nicht benannt werden
auto k() post(r : r >= 0)       // OK, „k“ ist eine Definition
{
    return 0;
}

Vertragskonsistenz

Eine Neudeklaration D einer Funktion oder Funktionsvorlage func muss entweder keine Vertragsspezifikationen oder dieselben Vertragsspezifikationen wie jede erste Deklaration F aufweisen, die von D aus erreichbar ist. Wenn D und F sich in verschiedenen Übersetzungseinheiten befinden, ist eine Diagnose nur erforderlich, wenn D an ein benanntes Modul angehängt ist.

Wenn eine Deklaration F1 eine erste Deklaration von func in einer Übersetzungseinheit ist und eine Deklaration F2 eine erste Deklaration von func in einer anderen Übersetzungseinheit ist, F1 und F2 müssen dieselben contract-specs  angeben, keine Diagnose erforderlich.

Zwei contract-specs sind gleich, wenn sie aus denselben Funktionskontraktspezifizierern in derselben Reihenfolge bestehen.

Ein Funktionsvertragsspezifizierer C1 auf einer Funktionsdeklaration D1 ist derselbe wie ein Funktionsvertragsspezifizierer C2 auf einer Funktionsdeklaration D2 wenn alle folgenden Bedingungen erfüllt sind:

  • Die Prädikate von C1 und C2 würden die One-Definition-Rule erfüllen, wenn sie in Funktionsdefinitionen an den Deklarationen D1 und D2 platziert würden (falls D1 und D2 in verschiedenen Übersetzungseinheiten sind, verhalten sich entsprechende Entitäten innerhalb jedes Prädikats wie eine einzelne Entität mit einer einzigen Definition), außer für folgende Umbenennungen:
    • Die Umbenennung der Parameter der deklarierten Funktion.
    • Die Umbenennung von Template-Parametern eines das deklarierte Funktionstemplate umschließenden Templates.
    • Die Umbenennung der Ergebnisbindung (falls vorhanden).
  • Sowohl C1 als auch C2 haben einen Bezeichner oder keiner von beiden hat einen.

Wenn diese Bedingung allein aufgrund des Vergleichs zweier Lambda-Ausdrücke, die innerhalb der Prädikate enthalten sind, nicht erfüllt ist, ist keine Diagnose erforderlich.

bool b1, b2;
void f() pre (b1) pre([]{ return b2; }());
void f(); // OK, Funktionsvertragsspezifizierer ausgelassen
void f() pre (b1) pre([]{ return b2; }()); // Fehler: Closures haben unterschiedliche Typen
void f() pre (b1); // Fehler: Funktionsvertragsspezifizierer sind unterschiedlich
int g() post(r : b1);
int g() post(b1); // Fehler: Keine Ergebnisbindung
namespace N
{
    void h() pre (b1);
    bool b1;
    void h() pre (b1); // Fehler: Funktionsvertragsspezifizierer unterscheiden sich
                       //        gemäß der One-Definition-Rule
}
(seit C++26)

Hinweise

Im Falle von Mehrdeutigkeit zwischen einer Variablendeklaration mit der Direktinitialisierungssyntax und einer Funktionsdeklaration wählt der Compiler immer die Funktionsdeklaration; siehe direct-initialization .

Feature-Test-Makro Wert Std Feature
__cpp_decltype_auto 201304L (C++14) decltype(auto)
__cpp_return_type_deduction 201304L (C++14) Rückgabetyp-Deduktion für normale Funktionen
__cpp_explicit_this_parameter 202110L (C++23) explizite Objektparameter ( deducing this )
__cpp_deleted_function 202403L (C++26) gelöschte Funktion mit Begründung

Schlüsselwörter

default , delete , pre , post

Beispiel

#include <iostream>
#include <string>
// einfache Funktion mit einem Standardargument, gibt nichts zurück
void f0(const std::string& arg = "world!")
{
    std::cout << "Hello, " << arg << '\n';
}
// die Deklaration befindet sich im Namensbereich (Dateibereich)
// (die Definition wird später bereitgestellt)
int f1();
// Funktion, die einen Zeiger auf f0 zurückgibt, pre-C++11 Stil
void (*fp03())(const std::string&)
{
    return f0;
}
// Funktion, die einen Zeiger auf f0 zurückgibt, mit C++11 Trailing Return Type
auto fp11() -> void(*)(const std::string&)
{
    return f0;
}
int main()
{
    f0();
    fp03()("test!");
    fp11()("again!");
    int f2(std::string) noexcept; // Deklaration im Funktionsbereich
    std::cout << "f2(\"bad\"): " << f2("bad") << '\n';
    std::cout << "f2(\"42\"): " << f2("42") << '\n';
}
// einfache Nicht-Member-Funktion, die int zurückgibt
int f1()
{
    return 007;
}
// Funktion mit einer Exception-Spezifikation und einem Function-Try-Block
int f2(std::string str) noexcept
try
{
    return std::stoi(str);
}
catch (const std::exception& e)
{
    std::cerr << "stoi() failed!\n";
    return 0;
}
// gelöschte Funktion, ein Aufrufversuch führt zu einem Kompilierungsfehler
void bar() = delete
#   if __cpp_deleted_function
    ("reason")
#   endif
;

Mögliche Ausgabe:

stoi() failed!
Hello, world!
Hello, test!
Hello, again!
f2("bad"): 0
f2("42"): 42

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 135 C++98 in der Klasse definierte Memberfunktionen
konnten keinen Parameter ihres eigenen Typs haben
oder diesen zurückgeben, da er unvollständig ist
erlaubt
CWG 332 C++98 ein Parameter konnte cv-qualifizierten void Typ haben verboten
CWG 393 C++98 Typen, die Zeiger/Referenzen auf Arrays
unbekannter Größe enthalten, konnten keine Parameter sein
solche Typen sind erlaubt
CWG 452 C++98 Member-Initialisiererliste war kein Teil des Funktionsrumpfs ist es
CWG 577 C++98 abhängiger Typ void konnte verwendet werden,
um eine Funktion ohne Parameter zu deklarieren
nur nicht-abhängiger
void ist erlaubt
CWG 1327 C++11 defaulted oder deleted Funktionen konnten nicht
mit override oder final spezifiziert werden
erlaubt
CWG 1355 C++11 nur spezielle Memberfunktionen konnten user-provided sein auf alle Funktionen erweitert
CWG 1394 C++11 deleted Funktionen konnten keinen Parameter eines
unvollständigen Typs haben oder einen unvollständigen Typ zurückgeben
unvollständiger Typ erlaubt
CWG 1824 C++98 die Vollständigkeitsprüfung von Parametertyp und
Rückgabetyp einer Funktionsdefinition konnte
außerhalb des Kontexts der Funktionsdefinition erfolgen
nur Prüfung im
Kontext der
Funktionsdefinition
CWG 1877 C++14 Rückgabetyp-Deduktion behandelte return ; als return void ( ) ; leitet den Rückgabetyp
einfach als void in diesem Fall ab
CWG 2015 C++11 die implizite odr-Verwendung einer gelöschten
virtuellen Funktion war ill-formed
solche odr-Verwendungen sind
von der Nutzungseinschränkung ausgenommen
CWG 2044 C++14 Rückgabetyp-Deduktion bei Funktionen, die void zurückgeben,
würde fehlschlagen, wenn der deklarierte Rückgabetyp decltype ( auto ) ist
die Deduktionsregel
für diesen Fall aktualisiert
CWG 2081 C++14 Funktionsneudeklarationen konnten Rückgabetyp-
Deduktion verwenden, selbst wenn die ursprüngliche Deklaration dies nicht tat
nicht erlaubt
CWG 2144 C++11 { } konnte gleichzeitig ein Funktionsrumpf oder ein Initialisierer sein wird durch den Typ
des Deklarator-Identifiers unterschieden
CWG 2145 C++98 der Deklarator in der Funktionsdefinition konnte nicht in Klammern gesetzt werden erlaubt
CWG 2259 C++11 die Mehrdeutigkeitsauflösungsregel bezüglich geklammerter
Typnamen deckte keine Lambda-Ausdrücke ab
abgedeckt
CWG 2430 C++98 in der Definition einer Memberfunktion in einer Klassendefinition
konnte der Typ dieser Klasse nicht der Rückgabetyp oder
Parametertyp sein aufgrund der Auflösung von CWG Issue 1824
nur Prüfung im
Funktionsrumpf
CWG 2760 C++98 der Funktionsrumpf eines Konstruktors beinhaltete nicht die Initialisierungen,
die nicht im regulären Funktionsrumpf des Konstruktors spezifiziert waren
beinhaltet auch diese
Initialisierungen
CWG 2831 C++20 eine Funktionsdefinition mit einer Requires-Klausel
könnte eine nicht-templatisierte Funktion definieren
verboten
CWG 2846 C++23 explizite Objekt-Memberfunktionen konnten keine Out-of-Class-Definitionen haben erlaubt
CWG 2915 C++23 unbenannte explizite Objektparameter konnten Typ void haben verboten

Siehe auch

C-Dokumentation für Funktionsdeklarationen