【问题标题】:Why is this reference of a pointer a rvalue?为什么指针的这个引用是右值?
【发布时间】:2017-02-14 09:17:02
【问题描述】:

我有以下基类和继承类:

// 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 指针,它们通过指针引用传递。

为什么我会收到错误消息: “从 'Special_Task*' 类型的右值对类型 'Task&' 的非常量引用的初始化无效” 结合: “无法解析符号‘经理’”

对于 Special_Manager 的构造函数:

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

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

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

谢谢!

【问题讨论】:

  • 因为它不是左值,因为你不能分配给引用。
  • Special_Manager();里面的Manager_Special是什么???
  • 那是构造函数。我更正了类名中的输入错误。
  • 在这种情况下,您没有理由不能生成正确的minimal reproducible example。为什么不删除与问题无关的部分代码?您是否尝试将其减少到最小的测试用例?
  • 合适的 MCVE 示例:godbolt.org/g/YVxyKH - 看看如何编译它以给出(或多或少)您所询问的错误?我很想知道您是如何得到您声称的确切错误的。

标签: c++ pointers pass-by-reference rvalue


【解决方案1】:

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

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

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

从右值'Special_Task*'类型的非常量引用'Task*&'类型的初始化无效

(顺便说一句,我很想知道究竟是什么编译器/代码给了你这个消息。我尝试过的所有版本的 gcc 都给出了:'Task 类型的非常量引用的无效初始化*&' 来自 '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 * 并成为一个右值,它也不能分配给非常量引用。该示例还说明了为什么 const 引用是可以的:它会阻止导致 p 指向错误类型的对象的赋值。

回到你的问题:鉴于Manager 中的task 没有必要作为引用,简单的解决方法是将其声明更改为一个简单的指针:

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)

【讨论】:

  • “如错误消息所述” 不,如果您查看问题的 Markdown,您会发现这是格式错误。 OP 没有逃脱*
  • @LightnessRacesinOrbit 谢谢,已修复。在另一个错误消息中也有另一个丢失的*(不知何故),我也修复了这个问题。书面内容适用于实际的错误信息(即答案代表)。
  • 更改为 const 不允许我在执行期间分配 task = 0,对吗?或者,我可以按照您的说法在 Manager 类中使用普通的 Task 指针,避免在 Special_Manager 中使用额外的 Special_Task 指针,如果需要 Special_Task 的功能,只需对 Task 指针使用 reinterpretcast!?
  • @nubert 1/task(0) 不是问题(您应该忽略您由于某种原因已经接受的答案)。 task(0)taskSpecial_Manager 中的初始化,它是Special_Task * 类型,这是完全允许的。您无需更改它,将其更改为 const * 在这里无济于事。 2/ 您可以使用reinterpret_cast,但一般问题表明您的设计是错误的:我会将Task 中的task 字段替换为getTask() 虚拟成员函数@987654363 @ 可以覆盖。如果您需要帮助,请提出另一个问题。
  • @davmac task_id 只是用于识别任务,因为我在没有可用运行时信息的 MCU 上运行。此外,不支持 C++11,所以我想改变我的设计以避免 Special_Task 中的额外任务指针,从而避免将它作为右值引用传递解决了我的问题。谢谢!
【解决方案2】:

错误在这里:

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

0 被视为rvalueint&&,被强制转换为Task*&& 的类型。这是可能的,因为0 被认为是指向指针的可转换值的有效形式。但是因为构造函数只接受Task*&,它不能采用值0的强制转换形式,因此编译器会拒绝代码。

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

【讨论】:

  • 该错误与0无关。从代码中删除task (0),您仍然会得到相同的编译器错误。
  • 另外:如果您要使构造函数参数类型为 const Task*& - 是错误的。 Task * const & 是合适的类型。引用本身必须是const
【解决方案3】:

扩展 davmac 的答案:

  • 如果YX 的子类,则:

    • reference-to-X 确实可以引用Y。同样,
    • pointer-to-X可以指向Y

    然而,类型 pointer-to-Xpointer-to-Y 本身的关联方式与 X 和 @987654330 不同@ 是相关的,所以

    • pointer-to-pointer-to-X 不能指向 pointer-to-Y(尽管最后的 指针-to-X 可以指向Y),并且
    • reference-to-pointer-to-X 也不能引用 pointer-to-Y

比较模板实例,尽管XY 的关系std::vector<X>std::vector<Y> 根本不相关。


具体来说,reference-to-pointer-to-Task 不能引用 pointer-to-Special_Task

【讨论】:

    猜你喜欢
    • 2020-01-04
    • 1970-01-01
    • 2018-01-13
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    • 2021-01-16
    • 2015-12-13
    相关资源
    最近更新 更多