2017-02-14 17 views
3

У меня есть следующие основания и наследуемые классы:Почему эта ссылка указателя на 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; 
}; 

Идея заключается в том, чтобы указатель задача быть 0 для того, чтобы обнаружить, что ни текущая задача не выполняется. Чтобы иметь общий доступ к указателям Task и Special_Task, они передаются по ссылке на указатель.

Почему я получаю сообщение об ошибке: «недопустимой инициализации неконстантного ссылки типа„Task &“от RValue типа„Special_Task *“» в сочетании с: «Менеджером Symbol» 'не может быть решена»

для застройщиком Special_Manager:

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

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

с Special_Task * задача нормальный (указатель) VARI я не понимаю, почему это считается rvalue?

Спасибо!

+0

Потому что это не значение lvalue, потому что вы не можете назначать ссылки. – EJP

+0

Что такое 'Special_Manager();' внутри класса 'Manager_Special' ??? –

+0

Это Конструктор. Я исправил ошибку ввода в имени класса. – nubert

ответ

3

Поскольку задача Special_Task * является нормальной (указателем) переменной, я не понимаю, почему она считается rvalue?

Любое lvalue может быть преобразовано в rvalue посредством неявного преобразования. task - это значение lvalue, потому что это «имя переменной ... в области видимости» (см. cppreference on value categories), и оно преобразуется в rvalue в вашем примере.

Но вся вещь lvalue/rvalue в основном является красной селедкой в ​​этом случае. Проблема заключается в том, что вы пытаетесь присвоить указатель одного типа к опорному указателю другого типа, как сообщение об ошибке говорит:

недействительна инициализация неконстантного ссылки типа «Task * &» от RValue типа «Специальные и подчеркивание; Задача *».

(как в сторону, я бы хотел, чтобы точно знать, какой компилятор/код дал вам это сообщение Все версии GCC, что я пытался дать вместо : Неверная инициализация неконстантной ссылки типа «Задача» & 'из rvalue типа «Задача *»).

Несмотря на то, что Special_Task является производным типом Task, вы не можете сделать этого. Их соответствующие типы указателей не являются подтипами; a Special_Task * не является Task *, и поэтому Task *& -переменная не может быть назначена Special_Task *. (Некоторая путаница может возникнуть из-за того, что Special_Task * может быть неявно преобразован в Task *, однако важно отметить, что в этом случае результирующий указатель представляет собой rvalue, а не lvalue, что объясняет последнее сообщение об ошибке).

Для того, чтобы проиллюстрировать, почему вы не можете назначить Special_Task * к Task *& переменной, рассмотрим следующий пример:

// 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 

Приведенный выше пример показывает, почему не может назначить Special_Task * к Task *& переменной напрямую. Из-за этого в вашем коде Special_Task * неявно преобразуется в Task * и становится rvalue, который также не может быть присвоен неконстантной ссылке. В примере также показано, почему ссылка const будет в порядке: это предотвратит назначение, которое вызывает p, чтобы указать на объект неправильного типа.

Вернуться к вашей проблеме: видя, как нет необходимости task в Manager быть ссылка, простое исправление изменить свое заявление на простой указатель:

Task* task; 

И изменить конструктор:

Manager(Task* _task); 

альтернативное решение, хотя не один я рекомендовал бы, должен был бы изменить тип к const ссылке:

Task * const & task; 

Manager(Task* const & _task); 

Да, и еще одна вещь:

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

Это проходит неинициализированную значение task к Manager конструктора и затем инициализирует task в 0. Вместо этого вы должны написать:

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

_ ", поскольку сообщение об ошибке говорит« Нет, если вы посмотрите на Markdown вопроса, вы увидите, что это ошибка форматирования. OP не избежал '*'. –

+0

@LightnessRacesinOrbit спасибо, исправлено. Если бы в другом сообщении об ошибке еще не было '*', я также исправил это. Письменное содержание относится к фактическим сообщениям об ошибках (то есть к ответам). – davmac

+0

Изменение в const не позволило бы мне назначить задачу = 0 во время выполнения, правильно? В качестве альтернативы, я мог бы использовать обычный указатель Task в классе Manager, как вы заявляли, избегать дополнительного указателя Special_Task в Special_Manager и просто использовать reinterpretcast на указателе Task, если требуется функция Special_Task !? – nubert

1

Ошибка здесь:

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

0 считается rvalue или int&& что отливают к типу Task*&&. Это возможно, потому что 0 считается действительной формой для значения, способного к перемещению, указателю. Но поскольку конструктор принимает только Task*&, он не может взять литую форму значения 0 и, таким образом, компилятор отклоняет код.

Если вы должны были сделать конструктор аргумент типа const Task*&, код будет затем скомпилировать, потому что const Task*& совместим с типом Task*&&.

+2

Ошибка не имеет ничего общего с '0'. Удалите 'task (0)' из кода, и вы все равно получите ту же ошибку компилятора. – davmac

+0

Также: _ Если вы должны были сделать аргумент конструктора типа const Задача * & _ - неверна. 'Task * const &' будет подходящим типом. Сама ссылка должна быть 'const'. – davmac

1

Расширить ответ от davmac:

  • если Y подкласс X, то:

    • ссылки-to X действительно может относиться к Y. Аналогичным образом,
    • a указатель-к-X может указывать на Y.

    Однако типы указатель-to X и указатель-to Y сами не связаны таким же образом X и Y связаны, так

    • pointer- to-pointer-to- X не может указывать на указатель-на-Y (хотя окончательный указатель-на-может указывают на Y) и
    • ссылка к указатель-to X не может ссылаться на указатель-to Y либо.

Сравнить экземпляры шаблона, где, несмотря на X и Y «S отношения std::vector<X> и std::vector<Y> являются не все связаны между собой.


В частности, ссылка к указатель-to Task не может ссылаться на указатель-to Special_Task.