【问题标题】:An intuitive reason why function's referenced parameter must be const in c++ [duplicate]c++ 中函数引用参数必须为 const 的直观原因 [重复]
【发布时间】:2021-05-23 10:45:51
【问题描述】:
我在看这段代码:
A foo() {
cout << "foo" << endl;
A a;
return a;
}
void bar(const A& a) {
cout << "bar" << endl;
}
int main() {
A a;
bar(a);
bar(foo());
}
并且试图理解为什么 c++ 语言如此急切地让我在通过 bar 一个临时对象时将 const 放在 A& a 的前面,就像我在 bar(foo()); 中所做的那样。
毕竟,如果可以访问临时对象,那为什么还要更改它对我来说如此糟糕?
有时我会传递一个lValue,bar()会根据需要更改它,有时我会发送一个rValue,bar()会浪费一点资源来更改它,然后,临时对象将被删除。什么是大问题?一些浪费的时钟周期?我可以忍受这一点!对吗?
为什么会有人关心我如何处理我的临时对象?他们很快就会被删除,他们的信仰与任何人无关..
【问题讨论】:
标签:
c++
reference
constants
pass-by-reference
temporary-objects
【解决方案1】:
答案是foo()!
当我传递 lValue 和传递 rValue 时,我太容易忘记或感到困惑 main()。
持有对真实对象的引用并对其进行更改(写入)是所需的功能!
并且持有对临时对象的引用并对其进行更改 - 乍一看 - 无害!正确的?写入临时对象可能发生的最坏情况是什么?无论如何它很快就会消失..
这当然不是真的。
如果 c++ 允许我将非常量临时对象传递给 bar(),那么我有责任始终确保当我需要传递 lValue 时我不会错误地将临时对象传递给 bar()!
这是难以检测的错误的来源。所以最好是把程序员从自己身上救出来,不要让他有写入临时对象的能力,不是因为我们要保存临时对象,而是因为我们想在编译时间早期拯救程序员关于他传递的逻辑错误一个 rValue,当他真正想要更改一个 lValue 时。
例如,考虑以下代码:
A foo() {
cout << "foo" << endl;
A a;
return a;
}
A& dim(A& a) {
return a;
}
void bar( A& a) {
cout << "bar" << endl;
}
int main() {
A a;
bar(dim(a));
}
这里我们不需要使用 const,因为 bar() 现在从 dim() 获取对 lValue 的引用。
人类如何跟踪何时传递对 lValue 的引用以及何时传递对 rValue 的引用?他不能。
如果 c++ 允许方法接受对 rValue 的引用,这将成为一个真正难以调试的错误,并且不会使开发人员在任何使用方面受益。永远。
我相信,如果您现在正在阅读本文,希望您会像我一样 - 我不希望 c++ 允许我写入临时对象!没用的!它会使我的生活复杂化,迫使我检查自己是否某个方法返回了对 lValue 或 rValue 的引用。
这就是为什么const 在这里很重要,它区分了同一函数的两个版本:一个可以更改对象,一个不能
看这段代码:
class A {
public:
A() { cout << "A ctor" << endl; }
A(const A& o){ cout << "A cctor" << endl; }
~A() { cout << "A Dtor" << endl; }
};
A foo() {
cout << "foo" << endl;
A a;
return a;
}
A& dim(A& a) {
return a;
}
void bar( A& a) {
cout << "bar" << endl;
}
void bar(const A& a) {
cout << "const bar" << endl;
}
int main() {
A a;
bar(dim(a));
bar(foo());
}
这里我们有两个几乎相同的方法,都命名为bar,并且都接收到 A 实例的引用。
唯一的区别是其中一个可以更改参考,而其中一个不能。
调用bar(dim(a)) 将调用void bar( A& a)
其中bar(foo()) 将调用bar(const A& a)
所以最后一次:如果 c++ 允许我将 rValue 发送到可以更改该 rValue 的方法,并且如果我错误地使用了这个选项 - 这肯定是一个逻辑错误!因为没有理由更改 rValue。
逻辑错误比编译错误更难调试。