C / C ++中const提供了哪种优化? (如果有的话)

我知道在可能的情况下,应该在通过引用传递参数时使用const关键字,或者出于可读性原因使用指针。 如果我指定参数是常量,编译器可以做什么优化?

可能有几种情况:

function参数:

常量引用:

void foo(const SomeClass& obj) 

常量SomeClass对象:

 void foo(const SomeClass* pObj) 

和SomeClass的常量指针:

 void foo(SomeClass* const pObj) 

variables声明:

 const int i = 1234 

函数声明:

 const char* foo() 

什么样的编译器优化每个提供(如果有的话)?

[读者请注意,这篇文章的大部分内容已经从Herb Sutter的一篇文章中解除 – http://www.gotw.ca/gotw/081.htm – 没有OP的归属。]

情况1:-

当你在程序中声明一个const时,

 int const x = 2; 

编译器可以通过不为这个variables提供存储而是将其添加到符号表中来优化这个常量。 所以,后续的读取只需要间接进入符号表而不是从内存中获取值的指令。

注意: – 如果你做下面的事情: –

 const int x = 1; const int* y = &x; 

然后这将迫使编译器为'x'分配空间。 所以,这种情况下,优化的程度是不可能的。

在函数参数const意味着参数在函数中不被修改。 据我所知,使用const没有实质性的好处,而是一个确保正确性的手段。

CASE_2: –

“是否将参数和/或返回值声明为const帮助编译器生成更优化的代码?”

  const Y& f( const X& x ) { // ... do something with x and find a Y object ... return someY; } 

Ques =>编译器能做的更好吗?

=>它可以避免参数的副本或返回值?

不,因为论据已经通过参考传递了。

=>是否可以将x或someY的副本放入只读内存?

不,因为x和y都超出了它的范围,来自和/或被赋予了外部世界。 即使有些是在f()本身内dynamic分配的,它和它的所有权也被放弃给调用者。

问=什么可能优化的代码出现在f()的主体内? 由于常量,编译器是否可以改进它为f()的主体生成的代码?

即使你调用一个const成员函数,编译器也不能认为对象x或对象someY的位不会被改变。 此外,还有其他问题(除非编译器执行全局优化):编译器也可能不知道其他代码是否可能具有与x和/或someY相同的对象的非const引用,以及是否有在执行f();函数时,可能会偶然使用对同一对象的非const引用。 编译器可能甚至不知道x和someY仅仅是引用的真实对象是否实际上首先被声明为const。

CASE_3: –

  void f( const Z z ) { // ... } 

Ques =>在这里会有什么优化吗?

是的,因为编译器知道z是一个const对象,即使没有全局分析,它也可以执行一些有用的优化。 例如,如果f()的主体包含像g(&z)这样的调用,编译器可以确定在调用g()期间z的非可变部分不会改变,

在给出任何答案之前,我想强调的是,使用const或不使用const的原因确实应该是为了程序的正确性,并且为了清晰起见,其他开发人员比编译器优化更清楚。 也就是说,使一个参数为const文档,该方法将不会修改该参数,并使一个成员函数的const文档,该成员将不会修改它所属的对象(至less不是在逻辑上改变了输出从任何其他的const成员函数)。 例如,这样做可以使开发人员避免不必要地复制对象(因为他们不必担心原始对象会被销毁或修改)或避免不必要的线程同步(例如,通过知道所有线程只是读取和执行不要改变所讨论的对象)。

就编译器的优化而言,至less在理论上,尽pipe处于优化模式下,编译器可以做出某些非标准的假设,可能会破坏标准的C ++代码,但应该考虑:

 for (int i = 0; i < obj.length(); ++i) { f(obj); } 

假设length函数被标记为const但实际上是一个昂贵的操作(假设它实际上在O(n)时间而不是O(1)时间内运行)。 如果函数f通过const引用获取其参数,那么编译器可能会优化此循环以:

 int cached_length = obj.length(); for (int i = 0; i < cached_length; ++i) { f(obj); } 

…因为函数f不修改参数的事实保证每次给定对象没有改变时length函数应该返回相同的值。 但是,如果f被声明为通过可变引用来引用参数,那么在循环的每次迭代中需要重新计算length ,因为f可能已经通过修改对象的方式来产生值的变化。

正如在注释中指出的那样,这是假设了一些额外的警告,并且只有在非标准模式下调用编译器时才有可能做出额外的假设(例如const方法严格地是其input的函数并且优化可以假设代码永远不会使用const_cast将const引用参数转换为可变引用)。

function参数:

const对引用的内存不重要。 这就好像把手放在优化器背后。

假设你在foo调用另外一个没有可见定义的函数(例如void bar() )。 优化器将有一个限制,因为它无法知道bar是否修改了传递给foo的函数参数(例如,通过访问全局内存)。 外部修改内存的可能性和别名对这个领域的优化器有很大的限制。

虽然你没有问,函数参数的const 确实允许优化,因为优化器保证了一个const对象。 当然,复制该参数的成本可能远远高于优化器的好处。

请参阅: http : //www.gotw.ca/gotw/081.htm


variables声明: const int i = 1234

这取决于声明的位置,创build时间和types。 这个类别主要是存在const优化的地方。 修改一个const对象或已知的常量是未定义的,所以编译器可以做一些优化; 它假定你不调用未定义的行为,并引入了一些保证。

 const int A(10); foo(A); // compiler can assume A's not been modified by foo 

显然,优化器也可以识别不变的variables:

 for (int i(0), n(10); i < n; ++i) { // << n is not const std::cout << i << ' '; } 

函数声明: const char* foo()

不重要。 引用的内存可能会被外部修改。 如果由foo返回的引用variables是可见的,则优化器可以进行优化,但是与函数的返回types的const的存在/不存在无关。

同样, const值或对象是不同的:

extern const char foo[];

SomeClass* const pObj创build一个指针types的常量对象。 不存在改变这种对象的安全方法,例如,即使地址被获取,编译器也可以将其caching到只有一个存储器读取的寄存器中。

其他人不具体做任何优化,虽然types上的const限定符会影响重载分辨率,并可能导致select不同的和更快的function。

const的确切作用因使用它的每个上下文而不同。 如果在声明一个variables的时候使用了const,那么它在物理上是const的,并且强有力地驻留在只读存储器中。

 const int x = 123; 

试图抛弃常量是未定义的行为:

尽pipeconst_cast可以从任何指针或引用中移除constability或volatility,但是使用结果指针或引用来写入已声明为const的对象或访问已声明为volatile的对象时会调用未定义的行为。 cppreference / const_cast会

所以在这种情况下,编译器可能会认为x的值总是123 。 这打开了一些优化潜力(常量传播)

对于function来说,这是另一回事。 假设:

 void doFancyStuff(const MyObject& o); 

我们的函数doFancyStuff可以用o做以下任何事情。

  1. 不修改对象。
  2. 强制转换,然后修改对象
  3. 修改MyObject的mutable数据成员

请注意,如果您使用被声明为const的MyObject实例调用函数,则将使用#2调用未定义的行为。

大师问题:下面会调用未定义的行为吗?

 const int x = 1; auto lam = [x]() mutable {const_cast<int&>(x) = 2;}; lam();