为什么这个指针的引用是一个右值?

时间:2017-02-14 09:17:02

标签: c++ pointers pass-by-reference 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和Spe​​cial_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 *任务是一个普通(指针)变量,我不明白为什么它被认为是右值?

谢谢!

3 个答案:

答案 0 :(得分:3)

  

由于Special_Task *任务是一个普通(指针)变量,我不明白为什么它被认为是一个右值?

任何左值都可以通过隐式转换转换为右值。 task是左值,因为它是“变量的名称......在范围内”(请参阅​​cppreference on value categories),并在示例中将其转换为右值。

但是在这种情况下,整个左值/右值的东西在很大程度上是一个红色的鲱鱼。问题是您尝试将一种类型的指针分配给不同类型的指针引用,如错误消息所示:

来自'Special_Task *' 类型的右值的'任务*&'类型的非常量参考无效初始化

(顺便说一下,我很想知道究竟是什么编译器/代码给了你那个消息。我试过的gcc的所有版本都改为:类型'的非const引用的无效初始化*&'来自'Task *'类型的右值。)

尽管Special_TaskTask的派生类型,但您无法执行此操作。它们各自的指针类型不是子类型; 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被视为rvalueint&&,其类型为Task*&&。这是可能的,因为0被认为是指针的可投射值的有效形式。但是因为构造函数只接受Task*&,所以它不能采用值0的转换形式,因此编译器会拒绝代码。

如果要创建类型为const Task*&的构造函数参数,则代码将进行编译,因为const Task*&Task*&&的类型兼容。

答案 2 :(得分:1)

扩展davmac的答案:

  • 如果YX的子类,则:

    • 引用 - X 实际上可以引用Y。类似地,
    • 指针指向X 指向Y

    然而,指针指向 - X 指针指向 - Y 的类型本身并不相同{{ 1}}和X是相关的,所以

    • 指针指针指向Y 无法指向指针指向 - X (尽管最终的指针指向 - Y 可以指向X),
    • 指向指针的引用 - Y 也无法引用指针指向 - X

比较模板实例,尽管YX的关系Ystd::vector<X>完全无关。

具体来说,指向指针的引用 - std::vector<Y> 不能引用指针指向 - Task