该消息说您尝试分配给不是左值的表达式。对于内置类型,您只能分配给左值(这就是名称的来源:左值 = 可以在赋值运算符左侧的值,而右值 = 必须在右侧的值赋值运算符)。
那么什么是左值或右值?考虑以下代码:
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
,因为它还返回 a T
(employee*
在您的情况下为 an)。这就是编译器抱怨的原因。
这种抱怨是有帮助的:即使编译器不会抱怨,代码也不会像你几乎肯定想要的那样。该return
语句返回对象的副本data[i]
。因此,如果该语句payment.at(i)=NULL;
已编译,实际发生的情况如下:
- 复制内部对象
data[i]
(或者您在代码中调用它的任何方式)并返回临时副本。
- 该语句分配了该临时副本,但保持原始对象
MyVector
不变。
- 临时副本被破坏,没有留下你的任务痕迹。
这几乎肯定不是你想要的。您想更改内部对象。为此,您必须返回对该对象的引用。引用指的是它被初始化的对象,而不是复制。相应地,即使被返回,引用也是一个左值(从 C++11 开始,还有第二种类型的引用,其行为不同,但我们不需要在这里关心它)。然后您更正的函数显示为
template<typename T>
T& MyVector<T>::at(int i)
{
return data[i];
}
并且使用该定义,payment.at(i)=NULL;
不仅可以编译,而且实际上可以执行您想要的操作:将内部存储的i
-th 指针更改payment
为NULL
.