Ein verwirrendes Detail über die Most Vexing Parse

Lesezeit: 6 Minuten

Ein verwirrendes Detail uber die Most Vexing Parse
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.

  • Es ist ein Parameter für eine Funktion.

    – Bo Persson

    10. August 2011 um 8:36 Uhr

  • Ich denke, sie stehen im Kontext der gestellten Frage (vielleicht hätte ich nicht sagen sollen exakt). Möchten Sie näher darauf eingehen?

    – John

    10. August 2011 um 8:37 Uhr

  • @Nawaz: istream_iterator<int>(cin) wird nicht als Ausdruck interpretiert, sondern als Parameterdeklaration, das ist der Punkt der Frage.

    – John

    10. August 2011 um 8:48 Uhr

  • @Nawaz, du hast es falsch verstanden. Ich habe nicht gesagt istream_iterator<int>(cin) verursachte das Problem. Das OP hat ausdrücklich gefragt, wie istream_iterator<int>(cin) könnte als Parameterdeklaration interpretiert werden. Das ist also die Frage, die ich beantwortet habe.

    – John

    10. August 2011 um 9:47 Uhr

  • @Nawaz, es ist kein Kontext erforderlich. Es ist immer die Deklaration von cinkann es kein temporäres Objekt sein. Sie haben sich einfach geirrt.

    – Johannes Schaub – litb

    10. August 2011 um 15:39 Uhr


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 idh 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.

  • Können Sie ein Beispiel geben, wo dies notwendig wäre?

    – Vorlagentypdef

    10. August 2011 um 8:28 Uhr

  • int *a[10] ist ein Array von 10 int-Zeigern, aber int (*a)[10] ist ein Zeiger auf ein Array von 10 Ganzzahlen (ich hoffe, ich habe das richtig herum verstanden).

    – John

    10. August 2011 um 8:32 Uhr

  • Ah, also ist es nicht so sehr “Sie brauchen es” als vielmehr “einige Typen erfordern es”. Ich habe Ihre Bemerkung falsch interpretiert als “es gibt Typen, bei denen es sich um einen Kompilierungsfehler handelt, wenn keine zusätzlichen Klammern eingefügt werden, die sonst nicht erforderlich wären.”

    – Vorlagentypdef

    10. August 2011 um 8:34 Uhr

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]

988620cookie-checkEin verwirrendes Detail über die Most Vexing Parse

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy