通过非 const 引用参数修改 const 引用参数
Modifying const reference argument via non-const reference argument
考虑以下代码:
#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 变量,both 被 a
和b
。由于 f
修改了 b
,它也修改了 a
,据推测它不应该修改
此代码在没有任何警告的情况下编译并打印 3
。如果我们单独跟踪每个变量,看起来 const-correctness 得到了尊重:c
是非常量,因此将它作为 const
ref 作为 a
传递是完全没问题的,并且也作为非常量引用 b
,在 f
的主体中我们修改了 b
,它是非常量,但不触及 a
,它是常量.但是,当 c
被用作 a
和 b
时,a
在 f
的主体内被修改,违反了 a
的假设] 是常量,即使没有显式调用 const_cast
。
我尽可能地简化了这个示例,但是人们可以很容易地想到不那么明显的用例(例如作用于非常量引用参数的 const
方法)。
我的问题是:
- 我们真的可以说上面的代码是常量正确的吗?
- 上述用例是已知模式吗?这被认为是不好的做法吗?
- 除了混淆 reader 之外,上述代码是否是技术问题的根源?例如未定义的行为,或者编译器执行错误的简化假设?
However, in main
, I pass the same variable, referenced both by a
and by b
. As f
modifies b
, it also modifies a
, which it supposedly shouldn't
当f
修改b
所指的内容时,它不会修改a
。它修改了 a
所指的内容,但这没关系,因为 b
不是 const
。只有当您尝试使用 a
修改 a
所指的内容时,您才会遇到问题。
Can we really say the above code is const-correct?
是的。您不修改 const 变量。
Other than being confusing to a reader, can the above code be the source of technical problems? Such as undefined behavior, or the compiler performing wrong simplifying assumptions?
不,您的代码是合法的,并且会在所有符合标准的编译器上产生相同的结果。
如果它不是 const
开头,则常量引用参数不会生成它引用 const
的内容。它所做的一切都会阻止您使用引用来修改对象。另一个指向该对象的指针或引用仍然可以改变它,只要它不是 const
本身。
从
的API角度来看
void f(int const& a, int& b),
f
承诺 不会通过 a
引用修改任何内容 ,因此尊重 上的 const 正确性=12=]。此外,它 通知 用户 b
,另一方面,很可能 被用来修改它寻址的对象; b
可能被记录为 [in, out]
参数,或者只是 [out]
参数,f
。如果 b
实际上从未用于修改它所指向的对象,而且没有其他设计理由成为非常量引用,那么,另一方面,可以说是(较弱的)违反f
.
的实现者的 const 正确性
用户如何使用或误用这个 API 超出了 f
本身 直接 担心的范围,特别是一旦它 API 设计选择已经做出。然而,任何面向用户的 API 都应该被设计成最小化(鉴于其设计限制)用户射击 him-/herself 脚的风险。例如。在这种情况下,值语义方法 int f(int const& a)
或 int f(int copy_in_a)
可用于为用户构建一个不同且更难滥用的界面。
是的,代码是常量正确的,不,这个特定的代码没有未定义的行为。你在这里写的是一个引用别名的简单案例,归结为pointer aliasing。
也就是说,像这样的别名通常是不可取的,因为对于程序员和编译器来说,推理起来确实更复杂。此外,这会阻止某些优化,尤其是处理内存块。
这确实是一种不好的做法,因为很多程序员会(错误地)假设 int const& a
的值在调用过程中确实是常量。考虑
void f(int const& a, int& b)
{
b = a+1;
if(something) b = a+2;
}
看到 b
得到 a+3
的值会非常令人惊讶,但如果 a
和 b
指向同一个变量就会发生这种情况.
考虑以下代码:
#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 变量,both 被a
和b
。由于f
修改了b
,它也修改了a
,据推测它不应该修改
此代码在没有任何警告的情况下编译并打印 3
。如果我们单独跟踪每个变量,看起来 const-correctness 得到了尊重:c
是非常量,因此将它作为 const
ref 作为 a
传递是完全没问题的,并且也作为非常量引用 b
,在 f
的主体中我们修改了 b
,它是非常量,但不触及 a
,它是常量.但是,当 c
被用作 a
和 b
时,a
在 f
的主体内被修改,违反了 a
的假设] 是常量,即使没有显式调用 const_cast
。
我尽可能地简化了这个示例,但是人们可以很容易地想到不那么明显的用例(例如作用于非常量引用参数的 const
方法)。
我的问题是:
- 我们真的可以说上面的代码是常量正确的吗?
- 上述用例是已知模式吗?这被认为是不好的做法吗?
- 除了混淆 reader 之外,上述代码是否是技术问题的根源?例如未定义的行为,或者编译器执行错误的简化假设?
However, in
main
, I pass the same variable, referenced both bya
and byb
. Asf
modifiesb
, it also modifiesa
, which it supposedly shouldn't
当f
修改b
所指的内容时,它不会修改a
。它修改了 a
所指的内容,但这没关系,因为 b
不是 const
。只有当您尝试使用 a
修改 a
所指的内容时,您才会遇到问题。
Can we really say the above code is const-correct?
是的。您不修改 const 变量。
Other than being confusing to a reader, can the above code be the source of technical problems? Such as undefined behavior, or the compiler performing wrong simplifying assumptions?
不,您的代码是合法的,并且会在所有符合标准的编译器上产生相同的结果。
如果它不是 const
开头,则常量引用参数不会生成它引用 const
的内容。它所做的一切都会阻止您使用引用来修改对象。另一个指向该对象的指针或引用仍然可以改变它,只要它不是 const
本身。
从
的API角度来看void f(int const& a, int& b),
f
承诺 不会通过 a
引用修改任何内容 ,因此尊重 上的 const 正确性=12=]。此外,它 通知 用户 b
,另一方面,很可能 被用来修改它寻址的对象; b
可能被记录为 [in, out]
参数,或者只是 [out]
参数,f
。如果 b
实际上从未用于修改它所指向的对象,而且没有其他设计理由成为非常量引用,那么,另一方面,可以说是(较弱的)违反f
.
用户如何使用或误用这个 API 超出了 f
本身 直接 担心的范围,特别是一旦它 API 设计选择已经做出。然而,任何面向用户的 API 都应该被设计成最小化(鉴于其设计限制)用户射击 him-/herself 脚的风险。例如。在这种情况下,值语义方法 int f(int const& a)
或 int f(int copy_in_a)
可用于为用户构建一个不同且更难滥用的界面。
是的,代码是常量正确的,不,这个特定的代码没有未定义的行为。你在这里写的是一个引用别名的简单案例,归结为pointer aliasing。
也就是说,像这样的别名通常是不可取的,因为对于程序员和编译器来说,推理起来确实更复杂。此外,这会阻止某些优化,尤其是处理内存块。
这确实是一种不好的做法,因为很多程序员会(错误地)假设 int const& a
的值在调用过程中确实是常量。考虑
void f(int const& a, int& b)
{
b = a+1;
if(something) b = a+2;
}
看到 b
得到 a+3
的值会非常令人惊讶,但如果 a
和 b
指向同一个变量就会发生这种情况.