提问人:Franz Ferdinand 提问时间:8/6/2023 最后编辑:Franz Ferdinand 更新时间:8/6/2023 访问量:100
以 const-reference 为参数的函数可以更改基础对象吗?
Can a function with a const-reference as its parameter change the underlying object?
问:
对非常量变量的常量引用可以转换为非常量引用,然后可以通过引用修改基础对象。 它是否也适用于函数声明中的 const-references?
也就是说,在下面的代码中,如果 'a' 最初不是 const,是否允许 func() 更改 'a'? 或者这会导致未定义的行为吗?
void func(const int& arg);
int main()
{
int a = 5; //non-const
func(a);
std::cout << a; //Can func modify 'a' through a const-cast and thus output ≠5?
}
我之所以问这个问题,是因为这会阻止编译器进行优化,因为它在计算 func 后被迫再次查找“a”的值;特别是如果 func 的定义在另一个翻译单元中。
答:
是的,它可以。
调用将进行具有“不必要”的引用:func
const
指向 cv 限定类型的指针或引用实际上不需要指向或引用 cv 限定的对象,但将其视为指向或引用;[...]
func
可以通过以下命令删除此“不必要”:const
const_cast
void func(const int& arg)
{
int& evil = const_cast<int&>(arg); // OK so far
evil = 123; // undefined behavior only if arg refers to a const object
}
在这种情况下,可以通过 进行修改,因为 不是 const 对象。但是,如果实际上是 ,那么这将是未定义的行为:func
a
evil
a
a
const
在常量对象生存期内修改常量对象的任何尝试都会导致未定义的行为。
通常,如果给函数一个引用或指针,它可以简单地或该引用到它想要的任何类型。但是,如果访问的对象类型与引用的对象类型不相似,则在大多数情况下,这是未定义的行为。访问权限可能未定义,或本身没问题。const_cast
reinterpret_cast
const_cast
reinterpret_cast
对编译器优化的影响
请看以下简化示例:
void func(const int&); // "Black box" function.
// The compiler cannot prove that func isn't modifying
// its arguments, so it must assume that this happens.
int main()
{
int a = 5;
func(a); // func may be modifying a.
return a; // The compiler cannot assume that this is 'return 5'.
}
通过优化,clang 输出:
main:
push rax
mov dword ptr [rsp + 4], 5
lea rdi, [rsp + 4]
call func(int const&)@PLT
mov eax, dword ptr [rsp + 4]
pop rcx
ret
此代码确实存在可以修改 .func
a
mov dword ptr [rsp + 4], 5
堆栈上的存储a
mov eax, dword ptr [rsp + 4]
调用后堆栈中的负载a
func
如果改为写入,则程序集以 结尾,并且不会溢出到堆栈上,因为它不能被 修改。这样效率更高。const int a = 5;
mov eax, 5
a
func
评论
const_cast