【问题标题】:Can the compiler not determine whether a variable is const by Itself?编译器不能自己判断一个变量是否为 const 吗?
【发布时间】:2015-06-28 13:14:12
【问题描述】:

我知道这么简单的函数会被内联:

int foo(int a, int b){
    return a + b;
}

但我的问题是,编译器不能自动检测到这与以下内容相同吗:

int foo(const int a, const int b){
    return a + b;
}

既然可以检测到,为什么我需要在任何地方输入const?我知道由于编译器的进步,inline 关键字已经过时了。 const 是不是也该这么做了?

【问题讨论】:

  • 您不必为编译器输入const,而是为自己输入。如果你试图改变它的值,你是在告诉编译器踢你。
  • 你的例子不是很好。在这种特殊情况下,当编译器将其降低为 SSA 形式时,它看到ab 未分配给并且非volatile,因此它们实际上是@987654330 @,并采取相应的行动。 const 在应用于 指针 或作为函数接口(其签名)的一部分时功能要强大得多,因为那时编译器可以依赖函数的 Promise 进行优化。跨度>
  • inline 已过时。它允许函数的多个定义存在于程序中而不违反 ODR。
  • @JonathanMee 编译器可以检测变量是否被修改。没有编译器是足够好的读心器来检测变量是否应该被修改,这就是const存在的原因。
  • @JonathanMee 不。如果您将函数定义放在名称空间范围内的标头中,请不要将其标记为inline,并将该标头包含在多个源文件中,您将得到一个链接器错误。如果您将其标记为inline,则不会出现该错误。

标签: c++ constants qualifiers


【解决方案1】:

您不会将const 作为未修改变量的结果。你使用const强制你不修改它。如果没有const,您可以修改该值。使用const,编译器会报错。

这是一个语义问题。如果值不应该是可变的,则使用const,编译器将强制执行该意图。

【讨论】:

  • 我删除了上一条评论;我想我更清楚你要去哪里。编译器可以强制执行const 的事实也证明无论您是否指定const,它都可以进行相同的优化。我猜大多数编译器会因此为指定的const 和未指定的const-but-not-modified-anyway 生成完全相同的代码。
  • 是的,这就是我引用inline 的原因,无论我们是否要求,编译器都会自行决定inline 函数。我对const 也是这么说的。我不再声明函数inline,因为无论如何编译器都比我更清楚。为什么要声明变量const
  • 出于我所说的原因,您将指定const。即使const 相关的优化发生,无论您是否指定const,还有其他更好的理由来指定它。指定const主要 原因是您的意图,就像我在回答中所说的那样。如果您的意思是 const,则指定 const
【解决方案2】:

是的,编译器可以在您的示例中证明 constness。

不,这没有用:-)。

更新: Herb Sutter 将他的一个陷阱专门用于该主题 (http://www.gotw.ca/gotw/081.htm)。总结:

  • const 通过让编译器和链接器为 const 对象选择函数(包括 const 成员函数)来提供最大帮助。
  • const 对通常的翻译单元模型没有帮助[与我的设想不同];编译器需要查看整个程序以验证事实常量(仅声明并不能保证)并利用它,以及证明不存在别名...
  • ...编译器可以看到整个程序并且可以证明事实的 constness 它实际上当然 不再需要 const 声明了!它可以证明给我看。呵呵。
  • const 产生重大影响的一个地方是定义,因为编译器可能会将对象存储在只读内存中。

这篇文章当然值得一读。

关于整个程序的优化/翻译,这通常是利用 constness 所必需的,参见。下面的 cmets 来自 amdn 和 Angew。

【讨论】:

  • 集成的“complinkers”已经到来,而且并不罕见 :-) gcc.gnu.org/onlinedocs/gccint/LTO-Overview.html
  • @amdn 感谢您提供信息。将其整合到我的答案中。
  • VS 工具链也有 LTCG(链接时代码生成)。我会说大多数(如果不是全部)现代编译器都会这样做。
  • @PeterSchneider 这是一个了不起的链接,如果可以的话,我会再给你一个 +1。在列出了const 可以使代码受益的可能方式之后,Herb Sutter 总结道:“即使在优化方面,const 仍然主要用作让人类类设计人员更好地实现手工优化的工具,而不是全知编译器自动生成更好代码的标签。”
  • 我喜欢 gotw。但后来我想知道,除了语言律师之外,一种保证超过 100 个通常不平凡的陷阱的语言是否适合任何人。
【解决方案3】:

编译器不能自动检测到这与...相同吗

如果你的意思是编译器是否可以检测到变量在第二种情况下没有被修改,很可能是的。编译器可能会为两个代码示例生成相同的输出。但是,const 可能会在更复杂的情况下帮助编译器。但最重要的一点是它可以防止无意中修改其中一个变量。

【讨论】:

  • 您能否解释一下编译器无法检测到变量是const 的情况?我意识到你用“复杂的情况”来修改它。所以也许很难确定这种情况,我只是希望亲眼看到一些东西。
  • @JonathanMee 很可能是不同的翻译单元。 C/C++ 编译器模型(与例如 c# 模型相反)中的编译器无权访问目标文件,即使它们包含足够的类型信息。它无法知道不同 TU 中的函数不会修改 ref 或指针传递的参数。 (链接器会,但是对于代码生成来说为时已晚,除非一个有多个编译/链接运行,如运行时优化信息,或者一个不寻常的集成“编译器”)。
  • @PeterSchneider 这实际上是我迄今为止看到的最好的答案。先生,如果您可以将其输入为答案,我愿意接受。
【解决方案4】:

编译器将始终知道您做了什么,并从中推断出内部常量以优化代码。

编译器可能永远知道你想要做什么

如果您希望一个变量保持不变,但后来在代码中意外更改了它,编译器只能在您告诉编译器您想要的内容时捕获此错误。

这就是 const 关键字的用途。

【讨论】:

    【解决方案5】:
    struct bar {
      const int* x;
    };
    
    bar make_bar(const int& x){
      return {&x};
    }
    
    std::map<int,bar> data;
    
    shuffle(data);
    

    要知道bar 在其生命周期内永远不会修改x(或导致它被修改),需要了解程序中bar 的每次使用,或者说,使x 成为指向@987654326 的指针@。

    即使有完美的整个程序优化(不可能存在:图灵机也不是完全可以理解的),动态链接意味着您无法在编译时知道如何使用数据。 const 是一个承诺,违背承诺(在某些情况下)可能是 UB。编译器可以使用该 UB 以忽略被破坏的承诺的方式进行优化。

    inline 并没有过时:它意味着它曾经做过的同样的事情,该符号的链接器冲突将被忽略,它温和地建议将代码注入调用范围。

    const 简化了某些优化(这可能使它们成为可能),并强制程序员执行某些事情(这有助于程序员),并且可以更改代码的含义(const 重载)。

    【讨论】:

      【解决方案6】:

      也许他可以,但 const 语句也适用于 。如果您将变量设置为 const 并在之后尝试分配新值,则会出现错误。如果编译器自己制作一个 var ,这是行不通的。

      【讨论】:

        【解决方案7】:

        Const 限定符是一种在作用域内强制执行变量行为的方法。如果您尝试在声明为 const 的范围内修改它们,它只会为编译器提供向您尖叫的方法。

        如果变量在声明时为 const,则它可能是真正的 const(意味着它被写入只读位置,因此编译器优化)。

        您可以提供您的第二个函数非常量变量,这些变量将在函数范围内变为“const”。

        或者您也可以通过强制转换绕过 const,因此编译器无法解析您的整个代码以试图确定 valuea 是否会在函数范围内更改。

        考虑到 const 限定符主要用于代码强制,如果变量是 const 或非 const,编译器将在 99% 的情况下生成相同的代码,那么不,编译器不应该自动检测 constness。

        【讨论】:

        • 这似乎是反对auto 的论据。如果代码最终还是一样,那么 const 有什么帮助呢? 我们为什么要写它呢?
        • Const 对于代码的执行很有用。在像这样的一个小函数中,这似乎不是一个很大的收获,但是在更大的上下文中(例如具有 const 函数的类,或接受 const 参数),它为代码提供了一种自我文档的方式(如果一个函数需要一个const 参数,则该函数的创建者声明他不会在内部修改该变量)。关于 auto :这用于避免指定明显的类型(例如:如果函数返回 namespace1::namespace2::namespace3::ClassA 类型,您宁愿只说 auto ,然后将所有这些都放入)。
        【解决方案8】:

        简短回答:因为并非所有问题都那么简单。

        更长的答案:您不能假设适用于简单问题的方法也适用于复杂问题

        确切答案: const 是一个意图。 const 的主要目标是防止您意外地做任何事情。如果编译器会自动添加 const 它只会看到该方法不是 const 并将其保留。使用 const 关键字会引发错误。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-01-01
          • 1970-01-01
          • 2011-08-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多