
Vorlagentypdef
Meine Frage ist, wie die folgende Zeile als Funktionsdeklaration analysiert werden kann:
vector<int> v(istream_iterator<int>(cin), istream_iterator<int>());
Ich verstehe die meisten Details des Most Vexing Parse und warum der zweite temporäre Iterator als ein Typ interpretiert werden kann, der eine Funktion ist, die einen Iterator zurückgibt und keine Argumente akzeptiert, aber was ich nicht verstehe, ist, warum der erste temporäre Iterator sein kann als Typ interpretiert. Welchen Typ stellt es dar? Mein Gedanke ist, dass es sich um eine Art Funktionstyp handeln würde, aber ich kann den Namen nicht erkennen cin
wird verwendet. Deklariert es, dass der Parameter ein ist istream_iterator<int>
genannt cin
? Wenn ja, bedeutet das, dass Sie die Namen von Argumenten willkürlich in Klammern zu Funktionen setzen können? Und wenn ja, warum?
istream_iterator<int>(cin)
ist genau das gleiche wie istream_iterator<int> cin
aber mit überflüssigen Klammern. Diese Deklarationssyntax wurde von C geerbt, und ich glaube, sogar der Erfinder von C (Ken Thompson?) hat sie als Fehler bezeichnet.
Habe ich schon gesagt, dass ich Clang (sehr) mag?
Probieren Sie einfach Folgendes aus (vereinfachter Code)
#include <vector>
void foo(std::vector<int>);
int main() {
std::vector<int> v(int(i), int());
foo(v);
}
In der neu umbenannten LLVM ausprobieren (Nun, es ging gerade von llvm-gcc zu clang).
Und Sie erhalten:
/tmp/webcompile/_21483_0.cc:6:21: warning: parentheses were disambiguated
as a function declarator
std::vector<int> v(int(i), int());
^~~~~~~~~~~~~~~
/tmp/webcompile/_21483_0.cc:7:3: error: no matching function for call to 'foo'
foo(v);
^~~
/tmp/webcompile/_21483_0.cc:3:6: note: candidate function not viable:
no known conversion from 'std::vector<int> (int, int (*)())'
to 'std::vector<int>' for 1st argument
void foo(std::vector<int>);
^
3 diagnostics generated.
Und deshalb hat @john recht, int(i)
wird interpretiert als int i
dh ein benannter Parameter für die Funktion.
Ja, es ist der Parametername. Und ja, Sie können eine Reihe von Klammern hinzufügen, weil Sie es manchmal tun müssen.
Wenn der Parameter ein Funktionszeiger ist, void (*f)()
das musst du so schreiben.
Die Leute, die den Standard schreiben, haben ihre kostbare Zeit nicht damit verbracht, genau die Fälle aufzuzeigen, in denen die Klammern erlaubt oder tatsächlich erforderlich sind, also sagt der Standard nur, dass Sie es tun kann habe sie.
Es gibt einen Abschnitt namens Ambiguity resolution
im Standard (2003), der solchen Syntaxen gewidmet ist. Ich denke, ich brauche es nicht weiter zu erklären, wenn Sie den Abschnitt selbst lesen, denn er ist sehr klar mit vielen Beispielen!
Also los gehts:
8.2 Mehrdeutigkeitsauflösung [dcl.ambig.res]
1 – Die Mehrdeutigkeit, die sich aus der Ähnlichkeit zwischen einem Function-Style-Cast und einer in 6.8 erwähnten Deklaration ergibt, kann auch im Kontext einer Deklaration auftreten. In diesem Zusammenhang besteht die Wahl zwischen einer Funktionsdeklaration mit einem redundanten Satz von Klammern um einen Parameternamen und einer Objektdeklaration mit einer Umwandlung im Funktionsstil als Initialisierer. Genau wie bei den in 6.8 erwähnten Mehrdeutigkeiten soll der Beschluss jedes Konstrukt, das möglicherweise eine Deklaration sein könnte, als eine Deklaration betrachten. [Note: a declaration can be explicitly disambiguated by a nonfunction-style cast, by a = to indicate initialization or by removing the redundant parentheses around the parameter name. ]
[Example:
struct S {
S(int);
};
void foo(double a)
{
S w(int(a)); // function declaration
S x(int()); // function declaration
S y((int)a); // object declaration
S z = int(a); // object declaration
}
—end example]
2 – Die Mehrdeutigkeit, die sich aus der Ähnlichkeit zwischen einer Umwandlung im Funktionsstil und einer Typ-ID ergibt, kann in verschiedenen Kontexten auftreten. Die Mehrdeutigkeit erscheint als Wahl zwischen einem Cast-Ausdruck im Funktionsstil und einer Deklaration eines Typs. Die Lösung ist, dass jedes Konstrukt, das in seinem syntaktischen Kontext möglicherweise eine Typ-ID sein könnte, als Typ-ID betrachtet werden soll.
3- [Example:
#include <cstddef>
char *p;
void *operator new(size_t, int);
void foo() {
const int x = 63;
new (int(*p)) int; // new-placement expression
new (int(*[x])); // new type-id
}
//4 - For another example,
template <class T>
struct S {
T *p;
};
S<int()> x; // type-id
S<int(1)> y; // expression (ill-formed)
//5 - For another example,
void foo()
{
sizeof(int(1)); // expression
sizeof(int()); // type-id (ill-formed)
}
//6 - For another example,
void foo()
{
(int(1)); //expression
(int())1; //type-id (ill-formed)
}
—end example]
7 – Eine weitere Mehrdeutigkeit entsteht in einer Parameterdeklarationsklausel einer Funktionsdeklaration oder in einer Typ-ID, die der Operand eines sizeof- oder typeid-Operators ist, wenn ein Typname in Klammern verschachtelt ist. 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 die Deklarator-ID. Die Lösung besteht darin, den Typnamen als einfachen Typbezeichner und nicht als Deklarator-ID zu betrachten.
[Example:
class C { };
void f(int(C)) { } // void f(int (*fp)(C c)) { }
// not: void f(int C);
int g(C);
void foo() {
f(1); //error: cannot convert 1 to function pointer
f(g); //OK
}
//For another example,
class C { };
void h(int *(C[10])); // void h(int *(*_fp)(C _parm[10]));
// not: void h(int *C[10]);
—end example]
9886200cookie-checkEin verwirrendes Detail über die Most Vexing Parseyes