C++编译错误:构造函数中非const引用初始值必须为左值
我正在编写一个程序,其中包含定义组件行为的Component虚类,该组件需通过总线访问设备寄存器。寄存器操作由独立于总线类型的Register虚类定义,派生类RegisterI2C实现了I2C总线的寄存器访问函数。ComponentI2C类对应使用I2C总线的组件,我在该类中定义component_reg函数时调用RegisterI2C的构造函数,编译时出现错误:initial value of reference to non-const must be an lvalue C/C++(461)。我尝试过多种类型转换但未找到解决方案,且无法将component_reg改为返回指针,恳请各位提供解决思路。
代码示例:
class Component; class Register { // defines access method to registers public: Register(Component *parent, int reg) : parent_(parent), register_(reg) {} virtual int read() = 0; // read the register Component *const parent_; // parent component int register_; // the register to operate on }; class Component { // defines what a component do.Uses Register functions virtual Register &component_reg(int reg) = 0; // return a Register we use for op on comp }; class I2C {}; // provides I2C busfunctions class ComponentI2C; class RegisterI2C : public Register { // implement Register func for I2C bus public: RegisterI2C(Component *parent, int reg) : Register(parent, reg) {} /* use I2C class methods through downcasting Component to ComponentI2C */ int read() override { return 0; } }; class ComponentI2C : public Component, public I2C { public: RegisterI2C &component_reg(int reg) override { return {this, reg}; }; // **PROBLEM HERE** };
编译错误的核心原因是:你试图返回一个临时对象的非const引用。{this, reg}构造的是匿名RegisterI2C临时对象,临时对象属于右值,C++不允许将非const引用绑定到右值——临时对象会在表达式结束后立即销毁,引用会变成悬空引用,触发未定义行为。
针对不能返回指针的限制,提供两种可行方案:
方案1:在ComponentI2C中缓存寄存器对象
如果每个寄存器对应唯一的RegisterI2C实例,用容器缓存已创建的对象,保证返回的是长期存在的实例引用:
#include <unordered_map> class ComponentI2C : public Component, public I2C { private: std::unordered_map<int, RegisterI2C> reg_cache_; public: RegisterI2C &component_reg(int reg) override { // 查找缓存,不存在则创建新对象 auto it = reg_cache_.find(reg); if (it == reg_cache_.end()) { it = reg_cache_.emplace(reg, RegisterI2C(this, reg)).first; } return it->second; } };
这种方式既满足返回引用的要求,又避免了悬空引用问题,适合大多数场景。
方案2:修改为返回const引用(只读场景适用)
如果你的场景只需要对寄存器进行读操作,不需要修改RegisterI2C对象,可以将虚函数改为返回const Register&,此时合法绑定临时对象:
// 修改Component类的虚函数定义 class Component { virtual const Register &component_reg(int reg) = 0; }; // 对应修改ComponentI2C的实现 class ComponentI2C : public Component, public I2C { public: const RegisterI2C &component_reg(int reg) override { return RegisterI2C(this, reg); } };
注意:临时对象的生命周期会延长到引用的生命周期结束,但该方案仅适用于只读场景,且禁止通过const_cast去除const属性修改对象,否则会触发未定义行为。
内容的提问来源于stack exchange,提问作者DrCoolZic

