【问题标题】:Overhead of const modifier for built-in types in C++C++ 中内置类型的 const 修饰符的开销
【发布时间】:2012-04-03 19:13:07
【问题描述】:

我更喜欢在我编写的函数的所有内置参数中添加const 修饰符。例如:

void foo(const int arg1, const double arg2);

对我来说比:

void foo(int arg1, double arg2);

在代码审查后,我被告知 const 修饰符在应用于整数和内置类型时会带来开销。这是真的吗?为什么?

谢谢,

【问题讨论】:

  • 它带来了 5 个字符的开销,const,我猜。
  • 是时候更新您的简历并寻找一份可以与有线索的人一起工作的工作了
  • 不,这是一个可怕的6!还有一个空间
  • @DavidHeffernan:那为什么还有 hvae const 限定符呢?我们通过值传递的任何方式,如果它打算让函数修改该值。
  • @JamesKanze: 在重构那些 a-little-too-complicated 函数时,我做的第一件事就是尽可能多地const 本地变量,然后我可以放他们(变量)不在我的脑海中。并非您在野外接触到的所有代码都如您所愿。

标签: c++ constants overhead


【解决方案1】:

它没有比typedef 更多的开销。你的同事错了。

如果你想说服他,打印出这两种变体的反汇编,并告诉你的同事它们是相同的

但是,将const 限定符添加到像这样的原始类型是完全没有意义和徒劳的。无论如何,它们都会被复制,并且修改它们没有害处。将它们设为 const 没有任何好处。

【讨论】:

  • 不一定没有意义(假设我们谈论的是函数定义而不是原型)。
  • 为什么不呢?它可以防止哪些错误?通过复制 int const 可以获得什么?
  • 与创建任何其他局部变量 const 相同(保护您免受自己的侵害、记录意图等)。
  • 我不太明白怎么做。目的是创建int 的副本。调用者意图中没有任何内容不允许或阻止被调用者修改该副本。它甚至可能有用。
  • @Oli:就像提问者所做的那样,将const 放在函数的非定义声明中是没有意义的(在jalf 的意义上)。您描述的意义在于将const 放在函数的定义中,但问题没有显示任何定义。让声明和定义在字节上相同,而不仅仅是等价,也可能有一些价值,在这种情况下,您也可以将它推到声明中并说这样做并非毫无意义。
【解决方案2】:

const 没有任何开销,我猜你的同事只是对它的使用感到困惑,因为它(不幸的是)并不常见。我个人更喜欢const 尽可能多的局部变量,因为它增加了可读性。

当然,反证总是很容易,采用以下程序并使用汇编输出进行编译:

#include <stdio.h>

void foo1(int a, double b)
{
  printf("Not const %d, %g\n", a, b);
}

void foo2(const int a, const double b)
{
  printf("Const %d, %g\n", a, b);
}


int main()
{
  for(int i = 0; i < 10; ++i)
  {
    foo1(i, 5.5 * i);
    foo2(i, 12.8 * i);
  }
  return 0;
}

为这些函数生成的汇编代码完全相同(使用VS2010 release-build):

对于foo1(没有const-说明符):

; 4    : {

push    ebp
mov     ebp, esp

; 5    :    printf("Not const %d, %g\n", a, b);

fld     QWORD PTR _b$[ebp]
sub     esp, 8
fstp    QWORD PTR [esp]
push    eax
push    OFFSET ??_C@_0BC@FACFPKBC@Not?5const?5?$CFd?0?5?$CFg?6?$AA@
call    DWORD PTR __imp__printf
add     esp, 16                 ; 00000010H

; 6    : }

对于foo2(带有const-说明符):

; 9    : {

push    ebp
mov     ebp, esp

; 10   :    printf("Const %d, %g\n", a, b);

fld     QWORD PTR _b$[ebp]
sub     esp, 8
fstp    QWORD PTR [esp]
push    eax
push    OFFSET ??_C@_0O@LOLEPDHC@Const?5?$CFd?0?5?$CFg?6?$AA@
call    DWORD PTR __imp__printf
add     esp, 16                 ; 00000010H

; 11   : }

【讨论】:

    【解决方案3】:

    这不是真的。

    与此无关,您不应将const 放入函数声明,因为它是一个实现细节:它仅限定函数范围内的local变量.所以你可以这样写:

    double foo(unsigned int a, double b);  // declaration
    
    double foo(unsigned int const a, double b)  // implementation
    {
        b *= a;
        return bar(b);   // silly example
    }
    

    【讨论】:

    • 这可能会导致混淆,因为声明和定义在视觉上有所不同。因为这个原因,我在这里不使用const,但我当然可以理解他的立场; const 的优势是否超过了由于外观不同而可能造成的混淆,这确实是一个非常主观的判断。
    【解决方案4】:

    在代码审查之后,我被告知 const 修饰符在应用于整数和内置类型时会带来开销。这是真的吗?为什么?

    您将代码提供给谁来审查它?初级程序员?

    上述说法不正确。恰恰相反。使用 const 可能会应用一些优化。

    【讨论】:

      【解决方案5】:

      这不是真的。1

      或者,更准确地说,我想不出它可能是真的原因。 const 所做的只是强制编译器检查您是否没有更改变量的值;但这是编译时检查。


      1。假设我们使用与运行时性能或编译代码大小相关的“开销”的传统定义。

      【讨论】:

      • 他没有说明他指的是哪种“开销”。我可以想到程序速度、程序大小、编译速度或代码可读性。
      • @UweKeim:确实如此。但在进一步限定之前,我将假设运行时开销(尽管我现在已经在我的回答中明确表示,谢谢!)。
      • 事实上,因为他的参数变成了 const 对象,编译器很有可能使用const 进行更多优化。 (在实践中,我希望在优化活动的情况下,大多数编译器都会检测到对象没有被修改,不管有没有 const。)
      • @JamesKanze:这会启用哪种优化?
      • @jalf 编译器知道const 对象一旦被初始化就不会改变值。实际上,大多数编译器都会认识到无论如何都是这种情况,但是如果您通过引用或通过指针传递参数到另一个函数,编译器无法确定该函数不会丢弃 const 并修改值,除非对象本身实际上是 const。
      【解决方案6】:

      不,那不是真的。

      我认为您/他们将 const 与通过引用传递混淆了。

      【讨论】:

        【解决方案7】:

        你的意思是什么类型的开销? 编译二进制文件的开销还是编译器的开销?我确信编译后的二进制文件对于您添加的第一个和第二个代码示例是相同的。对于编译器 - 也许。 const 添加了额外的条件,必须在编译时检查。

        【讨论】:

        • 系统地使用const 会增加文件大小。使用非常简单的语言并且没有优化,读取文件的时间可能是编译时间的重要部分,甚至是主要部分。然而,C++ 不符合“非常简单的语言”的条件,即使没有优化,模板类型推导之类的事情也会超过读取文件所需的时间。
        【解决方案8】:

        如果它强制你创建一个额外的局部变量,它可能会带来开销。

        没有常量

        void foo(const int arg1, double arg2)
        {
            if (arg1 == 1)
                arg2 += 5.0;
        
            // use arg2
        }
        

        使用常量

        void foo(const int arg1, const double arg2)
        {
            double arg2Copy;
            if (arg1 == 1)
                arg2Copy = arg2 + 5.0;
            else
                arg2Copy = arg2;
        
            // use arg2Copy
        }
        

        但这真的取决于编译器。如果您担心开销,您应该比较生成的代码。

        【讨论】:

        • 任何理智的编译器都应该将它们优化为相同。
        • 您为什么认为这是“开销”?声明一个额外的变量不会影响生成的代码
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-05
        • 2013-08-30
        • 1970-01-01
        • 2018-03-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多