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

时间:2014-12-14 05:27:35

标签: c++ c const compiler-optimization

我知道在可能的情况下,在通过引用或指针传递参数时,应尽可能使用const关键字。如果我指定参数是常量,编译器可以做的任何优化吗?

可能会出现一些情况:

功能参数:

常量参考:

void foo(const SomeClass& obj)

Constant SomeClass对象:

void foo(const SomeClass* pObj)

和SomeClass的常量指针:

void foo(SomeClass* const pObj)

变量声明:

const int i = 1234

函数声明:

const char* foo()

每个人提供哪种编译器优化(如果有的话)?

5 个答案:

答案 0 :(得分:28)

[读者请注意,这篇文章的大部分内容都来自Herb Sutter的一篇文章 - http://www.gotw.ca/gotw/081.htm - 没有OP的归属。]

<强> CASE_1: -

在程序中声明const时,

int const x = 2;

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

注意: - 如果您执行以下操作: -

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 =&GT;编译器可以做得更好吗?

=&GT;它可以避免复制参数或返回值吗?

不,因为参数已经通过引用传递。

=&GT;它可以将x或someY的副本放入只读内存吗?

不,因为x和y都生活在其范围之外,来自和/或被赋予外部世界。即使someY在f()本身内动态分配,它和它的所有权也会被放弃给调用者。

<强> QUES =&GT;那些出现在f()体内的代码的可能优化呢?由于const,编译器能否以某种方式改进它为f()体生成的代码?

即使调用const成员函数,编译器也不能假设对象x或对象someY的位不会被更改。此外,还有其他问题(除非编译器执行全局优化):编译器也可能不确定没有其他代码可能有非const引用,该引用将同一对象别名为x和/或someY,以及是否有任何此类在执行f()期间,可能会偶然使用对同一对象的非const引用;并且编译器甚至可能不知道x和someY仅仅是引用的真实对象实际上是否首先被声明为const。

<强> CASE_3: -

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

<强> QUES =&GT;这有什么优化吗?

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

答案 1 :(得分:11)

在给出任何答案之前,我想强调的是,使用或不使用const的原因应该是程序的正确性,以及其他开发人员的清晰度,而不是编译器优化;也就是说,使参数const记录该方法不会修改该参数,并使成员函数const记录该成员不会修改它所属的对象(至少不是以某种方式逻辑上改变任何其他const成员函数的输出)。例如,这样做允许开发人员避免制作不必要的对象副本(因为他们不必担心原件将被销毁或修改)或避免不必要的线程同步(例如,通过知道所有线程只读取和执行不要改变有问题的对象。)

就优化而言,编译器可以进行优化,至少在理论上,虽然在优化模式下允许它做出某些可能破坏标准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方法严格来说,它们是输入的函数,并且优化可以假设代码永远不会使用const_cast将const引用参数转换为可变引用。)

答案 2 :(得分:6)

  

功能参数:

const对引用的内存不重要。这就像在优化器后面绑一只手。

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

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

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


  

变量声明:const int i = 1234

这取决于它的声明位置,创建时间和类型。此类别主要是存在const优化的位置。修改const对象或已知常量是未定义的,因此允许编译器进行一些优化;它假设你没有调用未定义的行为并引入了一些保证。

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

显然,优化器还可以识别不变的变量:

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

  

功能声明:const char* foo()

不重要。可以在外部修改引用的存储器。如果foo返回的引用变量可见,则优化器可以进行优化,但这与函数返回类型上const的存在/不存在无关。 / p>

同样,const值或对象不同:

extern const char foo[];

答案 3 :(得分:3)

SomeClass* const pObj创建一个指针类型的常量对象。没有改变这种对象的安全方法,因此编译器可以将其缓存到只有一个存储器读取的寄存器中,即使它的地址被占用也是如此。

其他人不会特别启用任何优化,尽管类型上的const限定符会影响重载决策,并可能导致选择不同且更快的函数。

答案 4 :(得分:3)

const的确切效果因使用它的每个上下文而异。如果在声明变量时使用const,则它在物理上是const并且有效地驻留在只读存储器中。

const int x = 123;

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

  

尽管const_cast可以从任何指针或引用中删除constness或volatile,但是使用结果指针或引用来写入声明为const的对象或访问声明为volatile的对象会调用undefined行为。 cppreference/const_cast

因此,在这种情况下,编译器可能会认为x的值始终为123。这打开了一些优化潜力(常数传播)

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

void doFancyStuff(const MyObject& o);

我们的函数doFancyStuff可以使用o执行以下任何操作。

  1. 不修改对象。
  2. 抛弃常量,然后修改对象
  3. 修改MyObject的mutable数据成员
  4. 请注意,如果使用MyObject的实例调用我们的函数声明为const,那么您将使用#2调用未定义的行为。

    Guru问题:以下是否会调用未定义的行为?

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