2017-02-14 3 views
3

Ich habe die folgende Basis und geerbten Klassen:Warum ist diese Referenz eines Zeigers ein rvalue?

// Abstract base class Manager. 
class Manager 
{ 
public: 
    Manager(Task*& _task); 
protected: 
    // Reference of a pointer to the current task. 
    Task*& task; 
}; 

// Abstract base class Task. 
class Task 
{ 
    virtual task_rcodes_t run() = 0; 
protected: 
    uint8_t task_id; 
}; 

// Still abstract class Special_Task. 
class Special_Task : public Task 
{ 
public: 
    Special_Task(); 
}; 

// Class Special_Manager. 
class Special_Manager : public Manager 
{ 
public: 
    Special_Manager(); 
protected: 
    // Pointer to the current special task. 
    Special_Task* task; 
}; 

Die Idee ist, die Aufgabe Zeiger auf 0 sein, um zu ermöglichen, zu erkennen, dass keine aktuelle Aufgabe ausgeführt wird. Um einen gemeinsamen Zugriff auf die Zeiger "Task" und "Special_Task" zu erhalten, werden sie als Verweis auf den Zeiger übergeben.

Warum erhalte ich die Fehlermeldung: „ungültige Initialisierung einer nicht konstante Referenz des Typs‚Task- &‘von einem R-Wert vom Typ‚Special_Task *‘“ in Kombination mit: „Symbol‚-Manager "konnte nicht aufgelöst werden“

für den Konstruktor der Special_Manager:

// Constructor Manager 
Manager::Manager(Task*& _task) : task (_task) 
{} 

// Constructor Special_Manager 
Special_Manager::Special_Manager() : Manager(task), task (0) 
{} 

Da Special_Task * Aufgabe einen normalen (Zeiger) ist Vari kann ich nicht sehen, warum es als rvalue gilt?

Vielen Dank!

+0

Weil es kein Lvalue ist, weil Sie Referenzen nicht zuordnen können. – EJP

+0

Was ist das 'Special_Manager();' in der Klasse 'Manager_Special' ??? –

+0

Das ist der Konstruktor. Ich habe den Tippfehler im Klassennamen korrigiert. – nubert

Antwort

3

Da Special_Task * Aufgabe eine normale (Zeiger) Variable ist, sehe ich nicht, warum es als ein rvalue betrachtet wird?

Beliebiger L-Wert kann über eine implizite Konvertierung in einen R-Wert konvertiert werden. task ist ein Lvalue, weil es "der Name einer Variablen ... im Bereich" ist (siehe cppreference on value categories) und in Ihrem Beispiel in einen rvalue konvertiert wird.

Aber das ganze lvalue/rvalue-Ding ist in diesem Fall größtenteils ein redhering. Das Problem ist, dass Sie einen Zeiger von einem Typ in einen Zeigerverweis eines anderen Typs zuweisen versuchen, wie die Fehlermeldung sagt:

ungültige Initialisierung einer nicht konstanter Referenz vom Typ ‚Aufgabe * &‘ von rvalue vom Typ ‚Sonder & Underbar, Aufgabe *‘.

(so nebenbei, ich würde gerne wissen, was genau Compiler/Code, den Sie diese Nachricht gab Alle Versionen von gcc, die ich versucht habe, geben statt : ungültige Initialisierung von nicht-const Referenz des Typs 'Task * &' aus einem Rvalue des Typs 'Task *').

Obwohl Special_Task ist eine abgeleitete Art von Task, können Sie dies nicht tun. Ihre jeweiligen Zeigertypen sind keine Subtypen. a Special_Task * ist kein Task * und daher eine Task *& -variable kann nicht zugewiesen werden Special_Task *. (Eine gewisse Verwirrung kann aus der Tatsache resultieren, dass Special_Task * implizit in eine Task * konvertiert werden kann, jedoch ist es wichtig zu beachten, dass in diesem Fall der resultierende Zeiger ein rvalue ist, kein lvalue, was die letztere Fehlermeldung erklärt).

Um zu zeigen, warum Sie nicht ein Special_Task * zu einem Task *& Variablen zuweisen können, sollen Sie das folgende Beispiel:

// Other_Task is a second derived class of Task: 
class Other_Task : public Task { /* ... */ } 

Special_Task st; 
Special_Task *p = &st; // ok, p points to st 

Task *& tp = p; // if it were allowed: tp references p 

Other_Task ot; 
tp = &ot;  // now, p points to ot - which is the wrong type 

Das obige Beispiel zeigt, warum nicht direkt ein Special_Task * auf einen Task *& Variable zuweisen kann. Aus diesem Grund wird die Special_Task * in Ihrem Code implizit in eine Task * konvertiert und wird zu einem rvalue, der auch nicht einer nichtkonstanten Referenz zugewiesen werden kann. Das Beispiel zeigt auch, warum eine const Referenz in Ordnung wäre: Es würde die Zuweisung verhindern, die bewirkt, dass auf ein Objekt des falschen Typs zeigt.

Zurück zu Ihrem Problem: da gibt es keine Notwendigkeit für task in Manager ist eine Referenz, die einfache Lösung zu sein, ist seine Erklärung auf einen einfachen Zeiger zu ändern:

Task* task; 

und ändern Sie den Konstruktor:

Manager(Task* _task); 

eine alternative Lösung, wenn man nicht, dass ich empfehlen würde, wäre die Art zu einer const Referenz zu ändern:

Task * const & task; 

Manager(Task* const & _task); 

Oh, und noch etwas:

Special_Manager::Special_Manager() : Manager(task), task (0) 

Dieser übergibt die nicht initialisierte Wert von task zum Manager Konstruktor und dann initialisiert task auf 0 Stattdessen sollten Sie schreiben:

Special_Manager::Special_Manager() : Manager(0), task (0) 
+0

_ "wie die Fehlermeldung sagt" _ Nein, wenn Sie den Abschlag der Frage betrachten, sehen Sie, dass das ein Formatierungsfehler ist. OP ist dem '*' nicht entkommen. –

+0

@LightnessRacesinOrbit danke, behoben. Hatte ein weiteres "*" in der anderen Fehlermeldung (irgendwie) und ich habe das auch behoben. Der geschriebene Inhalt gilt für die tatsächlichen Fehlermeldungen (d. H. Die Antwort steht). – davmac

+0

Der Wechsel zu const würde mir nicht erlauben, Task = 0 während der Ausführung zuzuweisen, richtig? Alternativ könnte ich einen normalen Task-Pointer in der Manager-Klasse verwenden, wie Sie gesagt haben, vermeiden Sie einen zusätzlichen Special_Task-Pointer in Special_Manager und verwenden Sie einfach Neuinterpretation für den Task-Pointer, wenn Funktionalität von Special_Task benötigt wird !? – nubert

1

Der Fehler ist hier:

Special_Manager::Special_Manager() : Manager(task), task (0) 
{} 

0 eine rvalue oder eine int&& betrachtet wird, die auf die Art der Task*&& gegossen wird. Dies ist möglich, da 0 als gültige Form für einen auswertbaren Wert für einen Zeiger gilt. Aber da der Konstruktor nur eine Task*& nimmt, kann er die gegossene Form des Wertes 0 nicht annehmen und so lehnt der Compiler den Code ab.

Wenn Sie das Konstruktor Argument des Typs const Task*& zu machen, würde der Code kompiliert, weil const Task*& mit der Art der Task*&& kompatibel ist.

+2

Der Fehler hat nichts mit der '0' zu tun. Entfernen Sie 'task (0)' aus dem Code und Sie erhalten immer noch den gleichen Compilerfehler. – davmac

+0

Auch: _Wenn Sie das Konstruktorargument vom Typ const Task * & _ - falsch machen. 'Task * const &' wäre der geeignete Typ. Die Referenz selbst muss "const" sein. – davmac

1

So erweitern Sie die Antwort von davmac:

  • wenn Y ist eine Unterklasse von X, dann:

    • ein Referenz-to X wirklich zu einem Y verweisen. Ähnlich kann
    • ein Zeiger auf X auf eine Y zeigen.

    jedoch die Typen Zeiger-to-X und Zeiger-to-Y werden sich nicht auf die gleiche Art und Weise im Zusammenhang X und Y verwandt sind, so

    • ein Zeiger- to-pointer-to X kann nicht auf einen Zeiger auf Y zeigen (obwohl der letzte Zeiger auf Xkonnte Punkt zu einem Y) und
    • a Referenz-zu-Zeiger-to- X nicht entweder ein pointer-to Y beziehen.

Vergleichen Vorlage Fällen, in denen trotz X und Y ‚s Beziehung std::vector<X> und std::vector<Y> sind nicht verwandt.


Insbesondere wird ein Referenz-to-pointer-to Task nicht beziehen sich auf eine pointer-to Special_Task.

Verwandte Themen