C-Funktionsdeklaratoren
Zuallererst gibt es C. In C A a()
ist Funktionsdeklaration. Hat zum Beispiel putchar
die folgende Deklaration. Normalerweise werden solche Deklarationen in Header-Dateien gespeichert, aber nichts hindert Sie daran, sie manuell zu schreiben, wenn Sie wissen, wie die Funktionsdeklaration aussieht. Die Argumentnamen sind in Deklarationen optional, daher habe ich sie in diesem Beispiel weggelassen.
int putchar(int);
Auf diese Weise können Sie den Code wie folgt schreiben.
int puts(const char *);
int main() {
puts("Hello, world!");
}
Mit C können Sie auch Funktionen definieren, die Funktionen als Argumente verwenden, mit einer gut lesbaren Syntax, die wie ein Funktionsaufruf aussieht (gut, sie ist lesbar, solange Sie keinen Zeiger auf die Funktion zurückgeben).
#include <stdio.h>
int eighty_four() {
return 84;
}
int output_result(int callback()) {
printf("Returned: %d\n", callback());
return 0;
}
int main() {
return output_result(eighty_four);
}
Wie bereits erwähnt, erlaubt C das Weglassen von Argumentnamen in Header-Dateien, daher output_result
würde dies in Header- Dateien so aussehen.
int output_result(int());
Ein Argument im Konstruktor
Erkennst du das nicht? Nun, ich möchte Sie daran erinnern.
A a(B());
Ja, es ist genau die gleiche Funktionsdeklaration. A
ist int
, a
ist output_result
und B
ist int
.
Sie können leicht einen Konflikt von C mit neuen Funktionen von C ++ feststellen. Um genau zu sein, Konstruktoren sind Klassenname und Klammer und alternative Deklarationssyntax mit ()
anstelle von =
. C ++ versucht von Natur aus, mit C-Code kompatibel zu sein, und muss sich daher mit diesem Fall befassen - auch wenn es praktisch niemanden interessiert. Daher haben alte C-Funktionen Vorrang vor neuen C ++ - Funktionen. Die Grammatik der Deklarationen versucht, den Namen als Funktion abzugleichen, bevor sie zur neuen Syntax zurückkehrt, ()
wenn dies fehlschlägt.
Wenn eine dieser Funktionen nicht vorhanden wäre oder eine andere Syntax hätte (wie {}
in C ++ 11), wäre dieses Problem bei der Syntax mit einem Argument niemals aufgetreten.
Jetzt können Sie fragen, warum A a((B()))
funktioniert. Nun, lassen Sie uns output_result
mit nutzlosen Klammern erklären .
int output_result((int()));
Es wird nicht funktionieren. Die Grammatik erfordert, dass die Variable nicht in Klammern steht.
<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token
C ++ erwartet hier jedoch einen Standardausdruck. In C ++ können Sie den folgenden Code schreiben.
int value = int();
Und der folgende Code.
int value = ((((int()))));
C ++ erwartet, dass der Ausdruck in Klammern ... nun ja ... Ausdruck ist, im Gegensatz zu dem Typ, den C erwartet. Klammern bedeuten hier nichts. Durch Einfügen nutzloser Klammern wird die C-Funktionsdeklaration jedoch nicht abgeglichen, und die neue Syntax kann ordnungsgemäß abgeglichen werden (was lediglich einen Ausdruck erwartet, z. B. 2 + 2
).
Weitere Argumente im Konstruktor
Sicherlich ist ein Argument nett, aber was ist mit zwei? Es ist nicht so, dass Konstruktoren nur ein Argument haben könnten. Eine der integrierten Klassen, die zwei Argumente akzeptiert, iststd::string
std::string hundred_dots(100, '.');
Das ist alles gut und schön (technisch gesehen wäre es am ärgerlichsten, wenn es so geschrieben würde std::string wat(int(), char())
, aber seien wir ehrlich - wer würde das schreiben? Aber nehmen wir an, dieser Code hat ein ärgerliches Problem. Sie würden annehmen, dass Sie setzen müssen alles in Klammern.
std::string hundred_dots((100, '.'));
Nicht ganz so.
<stdin>:2:36: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
In file included from /usr/include/c++/4.8/string:53:0,
from <stdin>:1:
/usr/include/c++/4.8/bits/basic_string.tcc:212:5: error: initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
basic_string<_CharT, _Traits, _Alloc>::
^
Ich bin mir nicht sicher , warum g ++ versucht zu konvertieren char
zu const char *
. In beiden Fällen wurde der Konstruktor mit nur einem Wert vom Typ aufgerufen char
. Es gibt keine Überladung mit einem Argument vom Typ char
, daher ist der Compiler verwirrt. Sie fragen sich vielleicht, warum das Argument vom Typ char ist.
(100, '.')
Ja, ,
hier ist ein Kommaoperator. Der Kommaoperator akzeptiert zwei Argumente und gibt das Argument auf der rechten Seite an. Es ist nicht wirklich nützlich, aber es ist etwas, das für meine Erklärung bekannt sein muss.
Stattdessen wird der folgende Code benötigt, um die ärgerlichste Analyse zu lösen.
std::string hundred_dots((100), ('.'));
Die Argumente stehen in Klammern, nicht der gesamte Ausdruck. Tatsächlich muss nur einer der Ausdrücke in Klammern stehen, da es ausreicht, leicht von der C-Grammatik abzuweichen, um die C ++ - Funktion zu verwenden. Die Dinge bringen uns an den Punkt der Nullargumente.
Keine Argumente im Konstruktor
Möglicherweise haben Sie die eighty_four
Funktion in meiner Erklärung bemerkt .
int eighty_four();
Ja, dies wird auch von der ärgerlichsten Analyse beeinflusst. Es ist eine gültige Definition, die Sie höchstwahrscheinlich gesehen haben, wenn Sie Header-Dateien erstellt haben (und sollten). Das Hinzufügen von Klammern behebt das Problem nicht.
int eighty_four(());
Warum ist das so? Nun, ()
ist kein Ausdruck. In C ++ müssen Sie einen Ausdruck in Klammern setzen. Sie können nicht auto value = ()
in C ++ schreiben , da ()
dies nichts bedeutet (und selbst wenn dies der Fall wäre, wie ein leeres Tupel (siehe Python), wäre es ein Argument, nicht Null). Praktisch bedeutet dies, dass Sie die Kurzschrift-Syntax nicht verwenden können, ohne die Syntax von C ++ 11 zu verwenden {}
, da keine Ausdrücke in Klammern gesetzt werden müssen und die C-Grammatik für Funktionsdeklarationen immer gilt.
(B())
ist nur ein C ++ - Ausdruck, nichts weiter. Es ist keine Ausnahme. Der einzige Unterschied besteht darin, dass es unmöglich ist, es als Typ zu analysieren, und das ist es auch nicht.