首页 > 解决方案 > 我无法将“Type&”类型的非常量左值引用绑定到“Type”类型的右值

问题描述

这是我的课

    class DriverPoint
{
public:

  DriverPoint(){};
  DriverPoint (DriverPoint& dp) = default;
  DriverPoint(double lat, double lon)
  {
    _lat = lat;
    _lon = lon;
  }
  double _lat;
  double _lon;
};

// main
    DriverPoint driverPoint(lat, _long); 
        vector.push_back(driverPoint);

当我尝试编译它时

无法将“DriverPoint&”类型的非常量左值引用绑定到“DriverPoint”类型的右值</p>

标签: c++11vector

解决方案


将元素添加到 std 容器时,它会被复制(或移动,如果是右值)。

所以调用:vec.push_back(driverPoint)内部调用DriverPoint的复制构造函数。

有两个重载vector::push_back

(1) void push_back( const T& value );
(2) void push_back( T&& value );

(1) 新元素被初始化为值的副本。(2) 值被移动到新元素中。

重载 2 用于右值引用(因此它要求 T 为MoveInsertable,在这种情况下意味着具有移动构造函数)。请注意,这里不是转发引用,因为 T 是类的模板参数,而不是函数的模板参数。

您对push_back的调用转到第一个重载,因为传递的参数(对象driverPoint)是一个非常量左值,可以绑定到const 左值(参数const T&预期在push_back的重载 1 中)。等等……复制构造函数还没有被调用!但是,在vector::push_back内部,尝试通过将 T 的副本作为 const 发送到复制构造函数来创建 T 的副本,如果复制构造函数在其参数上缺少“const”,则此操作失败。

第 1 步 - 修复复制 ctor

当然,将“const”添加到复制构造函数的参数可以解决这个问题:DriverPoint(const DriverPoint& driverPoint)

但是,上面的修复不是正确的(等等,它是正确的,如果你想要一个复制构造函数,请确保你期望的参数是 const lvalue ref - 但在这种情况下,复制构造函数根本不需要......)。

第 2 步 - 删除复制 ctor

拥有不需要的复制构造函数的问题在于,您正在隐式删除默认的移动构造函数(以及其他遵循5 的规则)。

请注意,当前代码甚至不允许将右值添加到向量中:vec.push_back(DriverPoint{})也会因相同的编译错误而失败。

如果您将复制构造函数修复为:通过调用push_backDriverPoint(const DriverPoint& driverPoint)向向量添加左值和添加右值都可以,但是两者都将通过复制 ctor 而不是通过移动,因为您没有实现移动并且默认移动是隐式的如果您声明以下五个中的任何一个,则删除:析构函数、复制 ctor、复制分配、移动 ctor 或移动分配。

如果您希望添加复制 ctor,例如用于调试,但仍要保留默认移动操作,请添加:

DriverPoint(DriverPoint&&) = default; // move ctor
DriverPoint& operator=(DriverPoint&&) = default; // move assignment

零规则

使用不需要特别注意复制或销毁的类型的最佳方法,即不需要用户实现以下三个中的任何一个:析构函数 - 复制 ctor - 复制赋值运算符,是使用零规则- 做不声明五个中的任何一个 - 这将为您提供所有五个的默认值。

请注意,这是实际生产代码中的常见错误——声明析构函数、复制构造函数或赋值运算符,从而隐式删除移动操作。这可能会以非常安静的方式严重损害性能。Move 可以在许多地方使用,而不需要程序员的注意,例如 std 容器。如果它被隐式删除,则类型的内部字段(例如字符串、向量等)将始终被复制,即使可以移动也是如此。在 C++11 之前开始的代码库中尤其常见,当时尚未引入移动操作,但也可以在新代码中找到。


推荐阅读