C++ 为啥赋值运算符应该返回一个 const ref 以避免 (a=b)=c
Posted
技术标签:
【中文标题】C++ 为啥赋值运算符应该返回一个 const ref 以避免 (a=b)=c【英文标题】:C++ why the assignment operator should return a const ref in order to avoid (a=b)=cC++ 为什么赋值运算符应该返回一个 const ref 以避免 (a=b)=c 【发布时间】:2011-06-10 01:15:27 【问题描述】:我正在读一本关于 C++ 的书,更准确地说是关于运算符重载的书。
示例如下:
const Array &Array::operator=(const Array &right)
// check self-assignment
// if not self- assignment do the copying
return *this; //enables x=y=z
书中提供的关于返回 const ref 而不是 ref 的解释是为了避免像 (x=y)=z 这样的赋值。我不明白我们为什么要避免这种情况。我知道在此示例中首先评估 x=y ,并且由于它返回一个 const 引用,因此 =z 部分无法执行。但为什么呢?
【问题讨论】:
哪本书?这对我来说似乎是不必要的预防措施。我无法想象有人写(x=y)=z
——他们为什么要写?如果没有括号,x=y=z
将被解析为 x=(y=z)
,这非常合理,因此没有风险。
但是为什么呢?为什么它是一个常量引用?为什么按这个顺序执行?为什么不能将z
分配给 (x=y)?
@antronis:获得更好的 C++ 书籍。
这是deitel c++如何编程第7版
我同意这不是你通常会写的东西,但如果你的意思是(x = y) == z
并且打错了怎么办?我也不赞同那个特定的表达式,但是做更多的事情 const 有助于将运行时错误转化为编译时错误,这通常是有帮助的。
【参考方案1】:
没有必要避免这种情况,除非本书的目标读者是那些通常写成(x=y)=z
的程序员,而他们的意思是x=y=z
。在实践中,没有人在他们正常的头脑中写下这个,所以预防措施是完全没有必要的。它还禁止一些其他简洁的结构,例如 (x=y).nonConstMember()
,几乎没有人写,但在某些情况下可能有用(尽管它们不应该被过度使用)。
@ybungalobill 是对的,买一本更好的书。
【讨论】:
+1。程序员“不小心”写(x=y)=z
似乎与程序员在意为 x=y
时不小心写 x=y+system("rm -rf /")
一样可能(并且有必要防止)。
但我见过if ((x = y) = z) ...
,作者的意思是if ((x = y) == z) ...
。【参考方案2】:
我能看到的唯一原因是这本书是为了向 C 程序员(或对 C 理解优于 C++ 理解的作者)解释 C++ 而写的。因为对于 C 程序员来说,表达式(x = y) = z
对于内置类型是无效的,他可能会尝试使用它的用户定义类型来获得相同的行为。
但是,C 和 C++ 是不同的语言,在 C++ 中,表达式 (x = y) = z
即使对于内置类型也是有效的。因此,如果您希望用户定义的类型具有相同的行为,您应该在 operator =
中返回一个非常量引用。
我建议你买一本更好的书,一本不会混淆 C 和 C++ 的书。它们不是同一种语言,即使它们来自一个共同的基础。
【讨论】:
【参考方案3】:我会看看内置类型的行为。
在定义您自己的类型时,运算符的行为方式最好与内置类型相同。这样可以轻松地采用您的类,而无需深入研究您的代码以了解它们的行为与预期不同的原因。
所以如果我们看整数:
int main()
int x = 5;
int y = 6;
int z = 7;
(x = y) = z;
std::cout << x << " " << y << " " << z << "\n";
这适用于 y 不变且 x 被分配 7。在您的代码中,我希望您的赋值运算符以相同的方式工作。标准赋值运算符定义:
Array& Array::operator=(Array const& rhs)
/* STUFF */
return *this;
应该这样做就好了(假设 /* STUFF */ 是正确的)。
【讨论】:
【参考方案4】:据我所知,赋值运算符在惯用的 C++ 中不返回 const 引用。标准类型也不返回 const 引用。
std::string a, b, c;
(a = b).clear(); // no objection from compiler
我所有的自定义赋值运算符都返回了一个非常量引用。
如有疑问,请查看标准库。它不是完美无缺的,但它肯定会正确地处理这样的基本问题。
【讨论】:
Boost 人通常也返回可变引用。【参考方案5】:(x=y)
表示x.operator=(y)
,它返回对象x
。因此,(x=y)=z
表示(x.operator=(y)).operator=(z)
。括号中的表达式将x
设置为y
并返回x
,然后外部位将x
设置为z
。它没有像您所期望的那样将y
设置为z
,也没有像表达式x = y = z
那样设置。
这种行为是违反直觉的(赋值后它们应该都相等,对吧?);返回一个 const 引用使其成为不可能并避免了该问题。
【讨论】:
虽然这种行为听起来违反直觉,但这并不是一个“问题”,因为这是程序员有意为之(这就是他们使用括号的原因) 谁会期望(x=y)=z
将y
设置为z
?这是一个非常人为的“问题”,一本 C++ 书籍不应该花时间解释预防措施。 (x=5)=z
没有将 5
设置为 z
。
如果你期望赋值是关联的(就像比较一样),你会期望最后三个都是相等的。幼稚,但合理的第一眼解释。我同意这种形式一般不应该使用,也不应该在书中强调,但是帮助编译器帮助你不尽可能地在脚上开枪是很好的=)
关于赋值首先要理解的是它不是关联的。我看过的每一本命令式编程教科书都在第 1 章或第 2 章中解释了这一点。您对错误输入 (x=y) == z
的评论更有意义。
可能不直观。 但是 这正是它处理整数的方式。我认为模仿标准类型的行为比试图保护人们免受晦涩的情况更重要。以上是关于C++ 为啥赋值运算符应该返回一个 const ref 以避免 (a=b)=c的主要内容,如果未能解决你的问题,请参考以下文章