我有以下基类和继承类:
// 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指针进行公共访问,它们通过引用传递给它们。
为什么会收到错误消息: "类型'任务&'的非const引用的无效初始化来自类型' Special_Task *'"的左值 结合: "符号'经理'无法解决"
用于Special_Manager的构造函数:
// Constructor Manager
Manager::Manager(Task*& _task) : task (_task)
{}
// Constructor Special_Manager
Special_Manager::Special_Manager() : Manager(task), task (0)
{}
由于Special_Task *任务是一个普通(指针)变量,我不明白为什么它被认为是右值?
谢谢!
答案 0 :(得分:3)
由于Special_Task *任务是一个普通(指针)变量,我不明白为什么它被认为是一个右值?
任何左值都可以通过隐式转换转换为右值。 task
是左值,因为它是“变量的名称......在范围内”(请参阅cppreference on value categories),并在示例中将其转换为右值。
但是在这种情况下,整个左值/右值的东西在很大程度上是一个红色的鲱鱼。问题是您尝试将一种类型的指针分配给不同类型的指针引用,如错误消息所示:
来自'Special_Task *' 类型的右值的'任务*&'类型的非常量参考无效初始化
(顺便说一下,我很想知道究竟是什么编译器/代码给了你那个消息。我试过的gcc的所有版本都改为:类型'的非const引用的无效初始化*&'来自'Task *'类型的右值。)
尽管Special_Task
是Task
的派生类型,但您无法执行此操作。它们各自的指针类型不是子类型; Special_Task *
不是Task *
,因此Task *&
- 变量无法分配Special_Task *
。 (有些混淆可能是因为Special_Task *
可以隐式转换为Task *
这一事实,但重要的是要注意,在这种情况下,结果指针是一个右值,而不是一个左值,这解释了后一个错误信息)。
为了说明无法将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引用。该示例还说明了为什么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)
答案 1 :(得分:1)
错误在于:
Special_Manager::Special_Manager() : Manager(task), task (0)
{}
0
被视为rvalue
或int&&
,其类型为Task*&&
。这是可能的,因为0
被认为是指针的可投射值的有效形式。但是因为构造函数只接受Task*&
,所以它不能采用值0
的转换形式,因此编译器会拒绝代码。
如果要创建类型为const Task*&
的构造函数参数,则代码将进行编译,因为const Task*&
与Task*&&
的类型兼容。
答案 2 :(得分:1)
扩展davmac的答案:
如果Y
是X
的子类,则:
X
实际上可以引用Y
。类似地,X
指向Y
。然而,指针指向 - X
和指针指向 - Y
的类型本身并不相同{{ 1}}和X
是相关的,所以
Y
无法指向指针指向 - X
(尽管最终的指针指向 - Y
可以指向X
),Y
也无法引用指针指向 - X
。 比较模板实例,尽管Y
和X
的关系Y
与std::vector<X>
完全无关。
具体来说,指向指针的引用 - std::vector<Y>
不能引用指针指向 - Task
。