考虑以下代码:
#include <iostream>
void f(int const& a, int& b)
{
b = a+1;
}
int main() {
int c=2;
f(c,c);
std::cout << c << std::endl;
}
f
具有两个参考参数:int const& a
和int& b
。因此,应该f
不能修改a
,但是它可以 修改b
,并且确实可以。main
中,我传递了 same 变量,该变量分别由a
和b
引用。在f
修改b
的同时,它也修改了a
,这本来是不应该的此代码编译时没有任何警告,并显示3
。如果我们分别跟踪每个变量,则看起来像是遵循const-correctness:c
是非const,因此将其作为const
ref传递为a
是非常好的,并且也可以作为b
的非常量引用,并且在f
的正文中,我们修改了非常量的b
,而没有触摸const的a
。但是,当同时使用c
和a
时,b
在a
的主体内被修改,这违反了{{1} }是const,即使从未调用过任何明确的f
。
我尽可能简化了这个示例,但是可以很容易地想到不太明显的用例(例如,对非const引用参数起作用的a
方法)。
我的问题是:
答案 0 :(得分:30)
但是,在
main
中,我传递了相同的变量,该变量同时被a
和b
引用。在f
修改b
的同时,它也修改了a
,这本来不应该
当f
修改b
所指的内容时,它不会修改a
。它修改了a
所指的内容,但是没关系,因为b
不是const
。只有当您尝试通过使用a
来修改a
所指的内容时,才有问题。
我们真的可以说上面的代码是const正确的吗?
是的。您无需修改const变量。
除了使读者感到困惑之外,上述代码能否成为技术问题的根源?例如未定义的行为,或者编译器执行错误的简化假设?
不,您的代码是合法的,并且将在所有符合条件的编译器上产生相同的结果。
如果常量引用参数开头不是const
,则常量引用参数不会使它指向const
。它所做的一切都阻止您使用引用来修改对象。只要不是const
本身,指向该对象的另一个指针或引用仍然可以对其进行突变。
答案 1 :(得分:7)
是的,代码是const正确的,不,此特定代码没有未定义的行为。您在此处编写的是引用别名的简单情况,它可以简化为pointer aliasing。
话虽这么说,这样的别名通常是不希望的,因为对于程序员和编译器来说,这样做的理由确实更加复杂。而且,这会阻止某些优化,尤其是处理内存块。答案 2 :(得分:3)
从API的角度来看
void f(int const& a, int& b),
f
承诺不要通过a
引用修改任何内容,因此尊重a
上const的正确性。此外,它通知用户b
,另一方面,很有可能用于修改它要解决的对象; b
可能会被记录为[in, out]
的{{1}}参数,或者只是[out]
参数。如果f
实际上从未用于修改其寻址的对象,而且没有其他设计原因可以成为非const引用,则另一方面可以说是(较弱)违反b
的实现者的const正确性。
用户如何使用或滥用此API超出了f
本身直接 担心的范围,尤其是在确定了API设计之后。但是,可以说,任何面向用户的API都应设计为最大程度地降低(鉴于其设计约束)用户用脚射击自己的风险。例如。在这种情况下,可以使用值语义方法f
或int f(int const& a)
来构造面向用户的不同且难以滥用的界面。
答案 3 :(得分:1)
这确实是一个坏习惯,因为许多程序员会(错误地)假设int const& a
的值在调用期间确实是恒定的。考虑
void f(int const& a, int& b)
{
b = a+1;
if(something) b = a+2;
}
看到b
获得a+3
的值会非常令人惊讶,但是如果a
和b
指向同一个变量,就会发生这种情况。 / p>