2016-12-23 1 views
4
// Example program 
#include <iostream> 
#include <string> 
#include <utility> 

class A { 
public: 
    int x; 
}; 

class B { 
public: 
    B(A &&a) : m_a(std::move(a)) {} 
    A m_a; 
}; 

int main() 
{ 
    B var(std::move(A())); 
    // B var(A()); // does not compile why? 

    std::cout << var.m_a.x << "\n"; 

} 

Im obigen Snippet kompiliert die auskommentierte Zeile nicht. Die Fehlermeldung erscheint, dass es var wie eine Funktionsdeklaration behandelt. Selbst wenn A einen Parameter für Konstruktor hat, wird es immer noch wie eine Funktionsdeklaration behandelt. Gibt es eine Möglichkeit, es zu schreiben, damit es nicht wie eine Funktionserklärung behandelt wird? Die Verwendung von typename hilft in diesem Fall nicht.Gibt es eine Möglichkeit, eine Klasse mit konstruierter Klasse zu initialisieren, ohne std :: move zu verwenden?

Antwort

9

Dieses Problem ist bekannt als the most vexing parse, die genau beschreibt Ihre Situation, wo der Parser nicht wissen Sie wollen eine Funktionsdeklaration oder die Instanziierung eines Objekts. Es ist mehrdeutig in dem Sinne, dass für einen Menschen ein gegebenes Stück Code X, aber für den Compiler tut es eindeutig Y.

Ein Weg, um es zu bekommen ist die list initialization Syntax:

B var{A()}; // note the use of brackets 

Das ist nicht mehr zweideutig, und es wird den Konstruktor aufrufen, wie man wollte. Sie können es auch in A oder in beide verwenden:

B var(A{}); 
B var{A{}}; 

Nun, warum ist es nicht eindeutig? Nehmen Sie die Deklaration eines Funktionsparameter zum Beispiel, dass ein Zeiger funktionieren:

int foo(int (*bar)()); 

Hier wird der Parameter ist vom Typ Zeiger auf Funktion, die keine Argumente annimmt und hat Typ int zurück. Ein anderer Weg, Zeiger auf Funktion zu erklären ist durch die Klammern im declarator Weglassen:

int foo(int bar()); 

, die noch einen Zeiger auf den vorherigen funktionieren identisch erklärt. Da wir im Zusammenhang mit der Deklaration von Parametern stehen (parameter-declaration-clause), wird die Grammatik, die analysiert wird, eine type-id, die teilweise auf abstract-declarator aufgebaut ist. Daher können wir die Kennung entfernen:

int foo(int()); 

Und wir am Ende immer noch mit dem gleichen Typ.

Wenn das gesagt alle, lassen Sie uns Ihren Code untersuchen und vergleichen Sie es mit den obigen Beispielen:

B var(A()); 

Wir haben etwas, das wie eine variable Deklaration vom Typ sieht B mit A() initialisiert. So weit, ist es gut. Aber warte, du hast gesagt, das kompiliert nicht!

Die Fehlermeldung erscheint, dass es var wie eine Funktionsdeklaration behandelt.

var ist in der Tat eine Funktionsdeklaration, obwohl es Ihnen an erster Stelle nicht so aussah. Dieses Verhalten ist aufgrund [dcl.ambig.res]/1:

[...] die Auflösung jedes Konstrukt zu berücksichtigen, die möglicherweise eine Erklärung einer Erklärung sein könnten.

Und dieser Satz gilt hier. Im Rückblick auf den vorherigen Beispielen:

int foo(int()); 

, die genauso zweideutig wie Ihr Code: foo könnte möglicherweise eine Erklärung sein, so dass die Auflösung dieser als ein zu interpretieren ist. Ihr könnte möglicherweise auch eine Erklärung sein, so dass es die gleiche Auflösung hat.

Der Standard hat ein paar Beispiele für diese Fälle auf [dcl.ambig.res]/1, example #1, und gibt auch einige Tipps, wie sie auf [dcl.ambig.res]/1, note #1 eindeutig zu machen:

[Anmerkung: Eine Erklärung explizit durch Hinzufügen von Klammern um das Argument vereindeutigt werden kann . Die Ambiguität kann vermieden werden, indem die Initialisierungssyntax für die Kopie oder die Initialisierung der Liste verwendet wird oder indem eine Nichtfunktionsart verwendet wird. - Endnote ]

[Beispiel:

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 y((int)a);     // object declaration 
S z = int(a);     // object declaration 
} 

- Ende Beispiel ]

2

Stattdessen bauen Ihre A wie so

B var(A{}); 

und es wird nicht zu verwechseln für eine Funktionsdeklaration.

2
B var = A(); 

Vorausgesetzt, dass der B(A&&) Konstruktor nicht explicit ist (in Ihrem Fall ist es nicht, so ist es ein Arbeitsansatz).

Die

B var(A{}); 

es ist auch ein Arbeitsansatz Ihr Problem zu begegnen.

Der Compiler denkt Ihre ursprüngliche Linie eine Funktionsdeklaration ist, weil in C++, können Sie alles vor Ort erklären können, auch Funktionen oder Strukturen:

int f() 
{ 
    void f(); // You declare that function exists. 
    class C; // You declare a class called `C` exists. 
} 

In Ihrem Fall:

B var(A()); 

Der Compiler sieht, dass es eine Funktion namens var gibt, die eine B zurückgibt und eine Funktion empfängt, die keine Parameter empfängt und eine A zurückgibt.

Sie müssen dies mit einem der beiden oben dargestellten Ansätze abklären.

Verwandte Themen