【问题标题】:error C2106: '=' : left operand must be l-value错误 C2106:“=”:左操作数必须是左值
【发布时间】:2013-07-29 15:37:23
【问题描述】:

查看有关错误 C2106 的其他问题,我仍然不知道我的代码有什么问题。编译时出现以下错误:

c:\driver.cpp(99): error C2106: '=' : 左操作数必须是左值

c:\driver.cpp(169): 错误 C2106: '=' : 左操作数必须是左值

代码行如下:

payroll.at(i) = NULL; //Line 99
payroll.at(count++) = ePtr; //Line 169

我不明白为什么会抛出这个错误。在这个项目中,我将 driver.cpp 从一组员工对象指针更改为我制作的自定义 Vector 模板。我将 Vector 声明如下...

//Declare an Vector to hold employee object pointers
MyVector <employee*> payroll;

感谢任何帮助...

【问题讨论】:

  • MyVector::at 返回什么?
  • MyGuess : YourVector::at() 必须按值返回,这是问题所在。
  • @tohava 这就是它返回的内容...'return myArray[n];'除非引用大于向量大小,否则它会返回传递给函数的值。
  • @KQball:返回类型是什么(决定如何返回返回什么)?跨度>

标签: c++ templates vector


【解决方案1】:

引发此错误的原因与您不能执行以下操作的原因相同:

36 = 3;

您的Vector::at 版本应该返回引用而不是值。
左值之所以称为左值,是因为它们可以出现在赋值的左侧。右值不能出现在左侧,这就是我们称它们为右值的原因。您不能将3 分配给36,因为36 不是左值,它是右值,是临时的。它没有内存地址。出于同样的原因,您不能将NULL 分配给payroll.at(i)


你的定义:

template <class V> V MyVector<V>::at(int n)

应该是什么:

template<typename V> V& MyVector::at(std::size_t n)
template<typename V> const V& MyVector::at(std::size_t n) const

【讨论】:

  • 好吧,我正在查看文档,它说它返回了一个参考。你确定吗?
  • @LewsTherin 你是对的,但这是一个向量的自定义实现
  • 假设前向内存布局,at 的参数应该是unsigned
  • at 的参数应该是size_t,但这并不是这里最大的问题。
  • 我并不是说答案有问题,我的意思是它超出了原始问题的重点。很抱歉造成混乱。
【解决方案2】:

消息说您尝试分配给不是左值的表达式。对于内置类型,您只能分配给左值(这就是名称的来源:左值 = 可以在赋值运算符的 left 手边的值,而右值 = 必须是在赋值运算符的右侧)。

那么什么是左值或右值?考虑以下代码:

int a;
a = 3;

在这个赋值中a 是一个左值(如果不是,编译器会抱怨)。也就是说,表达式a 指的是一个可以修改的对象。另一方面,3 是一个右值,即基本上是一个值。当然不能分配给3;编译器会抱怨声明 3=a; 与您在代码中得到的消息完全相同。

所以作为一个第一近似值,左值表示一个对象,而右值表示一个值。请注意,这也适用于表单的赋值

a = b;

其中b 也是一个变量。这里发生的是所谓的左值到右值的转换:分配的不是对象b,而是它的当前值。

现在考虑以下情况:

int f();
f() = 3;

在这里你可能会争辩说函数f 确实返回了一个对象(如果你使用一些用户定义的类型,你甚至可以看到它的构造/销毁)。但是编译器仍然会抱怨您收到的消息。为什么?

好吧,即使你认为f 返回一个对象,它也是一个临时 对象,它会立即消失。因此,分配一个值没有多大意义,因为之后无论如何您都无法对它做任何事情。

因此这是第二条规则:

只要有一个表达式产生一个临时对象,C++ 就会将该表达式定义为右值。

现在我们来到MyVector::at() 的定义,您没有显示它,但根据错误消息,它可能看起来类似于:

template<typename T>
 T MyVector<T>::at(int i)
{
  return data[i];
}

这与上面的f 具有基本相同的形式,因为它还返回T(在您的情况下为employee*)。这就是编译器抱怨的原因。

这种抱怨是有帮助的:即使编译器不会抱怨,代码也不会像您几乎肯定想要的那样。 return 语句返回对象data[i]副本。因此,如果语句payment.at(i)=NULL; 已编译,实际发生的情况如下:

  1. 内部对象data[i](或者您在代码中调用它)被复制并返回临时副本。
  2. 该语句分配了该临时副本,但保持MyVector 中的原始对象不变。
  3. 临时副本被破坏,没有留下您的任务痕迹。

这几乎肯定不是您想要的。您想更改内部对象。为此,您必须向该对象返回一个 reference。引用指的是它被初始化的对象,而不是复制。相应地,即使返回,引用也是一个左值(从 C++11 开始,有第二种引用类型的行为不同,但我们在这里不需要关心)。然后你更正的函数显示

template<typename T>
 T& MyVector<T>::at(int i)
{
  return data[i];
}

根据该定义,payment.at(i)=NULL; 不仅可以编译,而且实际上可以执行您想要的操作:将payment 中内部存储的i-th 指针更改为NULL

【讨论】:

    【解决方案3】:

    您的函数MyVector::at(unsigned) 可能未正确声明,如下所示:

    T MyVector::at(unsigned i) { /* implementation detail */ }
    

    你想要的是它看起来像这样:

    T& MyVector::at(unsigned i) { /* implementation detail */ }
    

    注意引用参数 (&),它将通过引用返回任何元素并允许将表达式用作左值。

    真正的问题是你为什么不改用std::vector

    【讨论】:

    • 也可能是const 过载
    • 是的,它需要一个 const 重载。为了清楚起见,我保持简单。
    【解决方案4】:

    c++ 中的术语左值表示“左值”的类型不正确。由于您在其上使用赋值运算符,因此要成为正确的类型,它必须是可以分配新值的值,这意味着它不能是常量。您对payroll.at() 的调用很可能返回一个常量值,而不是对实际值的引用。尝试为其分配新值将导致左值错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多