【问题标题】:const int *p vs. int const *p - Is const after the type acceptable?const int *p 与 int const *p - 类型后的 const 是否可以接受?
【发布时间】:2010-11-02 13:12:40
【问题描述】:

我的同事对他提出的问题(12)的评价是 0 比 2,所以我想我会给他一个追赶的机会。

我们最近的分歧是关于在声明中将“const”放在何处的样式问题。

他认为它应该放在类型前面或指针后面。原因是这是其他人通常会做的事情,而其他样式很容易混淆。因此,指向常量int 的指针和指向int 的常量指针分别为:

const int *i;
      int * const i;

但是,我还是很困惑。我需要一致且易于理解的规则,而我能够理解“const”的唯一方法是它它正在修改的东西之后。有一个例外允许它在最终类型之前,但这是一个例外,所以如果我不使用它对我来说会更容易。

因此,指向常量int 的指针和指向int 的常量指针分别为:

int const * i;
int * const i;

另外一个好处是,以这种方式做事可以让更深层次的间接性更容易理解。例如,指向int 的常量指针的指针显然是:

int * const * i;

我的论点是,如果有人只是按照他的方式学习,他们将毫不费力地弄清楚上述内容的用途。

这里的最终问题是,他认为将const 放在int 之后是如此丑陋,对可读性如此有害,以至于应该在样式指南中禁止它。当然,我认为如果指南应该建议按照我的方式进行操作,但无论哪种方式,我们都不应该禁止一种方法。

编辑: 我得到了很多好的答案,但没有一个能真正直接解决我的最后一段(“终极问题”)。很多人都在争论一致性,但在这种情况下,这是不是很可取,以至于禁止另一种方式是个好主意,而不是仅仅阻止它?

【问题讨论】:

  • 还有一点很重要,什么更重要?它是一个 int 的事实,还是它是常量的事实?
  • @rlbond:这是一个很好的问题……太好了,我不确定我能不能回答。
  • @T.E.D.关于您的编辑:样式指南的意义何在?这是开发人员在闲暇时忽略的建议,还是他们应该遵循的规则?如果你只是不鼓励特定的风格,那不就意味着这是一个建议吗?
  • @atk:在这种情况下,我相信是后者。
  • @T.E.D.你为什么只在 C++ 上下文中问这个问题?我在这里看不出与 C 的区别。

标签: c++ c pointers coding-style constants


【解决方案1】:

最重要的是一致性。如果没有任何编码指南,那么选择一个并坚持下去。但是,如果您的团队已经有了事实上的标准,请不要更改它!

也就是说,我认为目前更常见的是

const int * i;
int * const j;

因为大多数人都会写

const int n;

而不是

int const n;

附注——阅读指针constness 的一种简单方法是阅读从右侧开始的声明。

const int * i; // pointer to an int that is const
int * const j; // constant pointer to a (non-const) int
int const * aLessPopularWay; // pointer to a const int

【讨论】:

  • 我不同意“也就是说,我认为到目前为止更常见的是”。否则同意。
  • 你认为有更多的人这样做吗?就个人而言,我经常看到 const type*。
  • 嗯 - 不是最科学的结果,但是,谷歌代码搜索有 41 个结果 'int const *' 和 824 个结果 'const int *'(使用 lang:c++ 搜索)
  • 一致性不是那么重要,更重要的是灵活性和多样性。
  • @finnw:我总是惊叹为什么人们坚持认为* 必须与任何东西相邻。写int * i;,不仅两个“阵营”都不会责怪你偏袒对方的观点,而且任何人忽视*的机会实际上比任何一个都少.
【解决方案2】:

有一类示例将const 放在类型的右侧也有助于避免混淆。

如果你在 typedef 中有一个指针类型,那么就不可能改变 to 类型的常量:

typedef int * PINT;
const PINT pi;

pi 仍然有int * const 类型,无论你在哪里写const,都是一样的。

【讨论】:

  • 我认为这并不令人困惑。您的示例是 const (int*),因为 typedef 充当分组 - 类似于 (int*) const。相反,指向 const 的指针是 (const int)* 或 (int const)*
  • 如果您可以查看 PINT 并说:“这是一个 typedef,因此 const 不适用于指向的成员,因为 知道这就是标准说关于 typedefs。”。如果 PINT 是一个宏怎么办?然后规则不同,CV 说明符将应用于 to 类型。在合理的范围内,您的代码应该清晰明了。
【解决方案3】:

我希望 B. Stroustrup 的风格和技术常见问题解答中的这个解释能给你一个明确的答案。

Bjarne Stroustrup's C++ Style and Technique FAQ


我个人更喜欢:

int const* pi;
int* const pi;

因为const 标识了打算为 const 的左侧标记。

当你像这样使用 smth 时,你肯定会保持相同的一致性:

int const* const pi;

而不是写不一致:

const int* const pi;

如果你有一个指向指针的指针等等会发生什么:

int const* const* const pi;

代替:

const int* const* const pi;

【讨论】:

    【解决方案4】:

    我在一次会议上 Bjarne Stroustrup 正在做一个演讲,他使用了类似 const int* i 的东西。有人问他为什么要用这种风格,他回答(释义):

    “当事情不变时,人们喜欢首先看到const。”

    【讨论】:

    • 遗憾的是,在一般情况下,他没有将该原则应用于“const”的语法,否则我们将不会进行此讨论。
    • Stroustrup 可能与“const”的语法没有太大关系,因为 C++ 只是从 C 中接管了它。我记得它不是 K&R C 的一部分,但被引入了或多或少符合 1990 年的标准。
    • @David C++ 可以追溯到 1983 年,而我认为是 70 年代后期的前身“C with classes”。 C++ 首先有 const,它是用 C90 添加到 C 中的
    • IIRC,C++ 在 C 考虑后得到了 const -- end 最终被拒绝 -- “只读”。
    • Bjarne 声称他发明了 const:research.att.com/~bs/bs%5Ffaq2.html#constplacement。本文还提到 Bjarne 最初认为是“只读的”。
    【解决方案5】:

    人们通常使用const int* blah,因为它的英文读起来很好。我不会低估它的用处。

    我发现int* const blah 变体非常罕见,因此将更常见的定义倒过来通常没有用处。总的来说,我不喜欢在一般情况下稍微模糊代码的任何东西,尽管在例外情况下它可能会提供一些名义上的好处。

    另见“如果 (1 == a)”。有些人真的很喜欢编写不是英文的代码。我不是那些人中的一员。

    实际上,const 背后的规则很简单。向左看,然后向右看。如此简单,以至于我认为它在样式指南中不值得过多关注。

    【讨论】:

    • 这个问题实际上是在争论中出现的。作为一个老 Ada 编码器,我觉得这很有趣。如果英语阅读不好的代码对您来说是个问题,那么您将无法使用 C 语法语言。它不是为类似英语的可读性而设计的,试图在它上面强加同样的东西只会让你发疯。
    • 作为一个老 C++ 编码器,我相信如果在常见情况下左侧使用 const 的小不一致对您来说是个问题,那么您使用 C 语法语言没有任何业务。它不是为了一致性而设计的,试图强加一些只会让你发疯。说真的,你和你的朋友已经浪费了更多的时间来争论这个问题,而不是选择你建议的任何一个标准。解雇任何不能理解这两种语法的人并完成它。
    • @T.E.D:我认为这是一个稻草人的论点。仅仅因为“完美”的可读性是不可能的,我们仍然可以努力让它更具可读性。特别是,当我们有选择的时候,为什么不让变量声明看起来和他们说的一样呢?
    • “...命名等等”。我想我可以接受。请注意,您必须使用第一人称现在完成时态来做到这一点。比“Blah 是一个常数整数”要尴尬得多。但公平就是公平。把它放在答案中,我会收回反对票(所以除非答案改变,否则我不会改变投票)
    • 嘿。根据维基百科,您使用的实际上是“现在连续”,而不是我的“现在简单”。我想您可以通过将其更改为“Declare a...”来使其具有强制性。
    【解决方案6】:

    如果只有变量和指向它们的指针可能是const,那可能就没那么重要了。但请考虑:

    class MyClass
    {
        public:
            int foo() const;
    };
    

    const 不可能写在任何其他地方,除了 跟踪它所指的函数。

    样式指南越短,开发人员就越有可能遵循它。最短的规则是:

    const 关键字总是跟踪无论它指的是什么。

    所以,我在这里为你的同事说 0:3。

    关于你的“终极问题”:为了风格指南,不管指南是“劝阻”还是“禁止”它公开反对的东西。这是一个社会问题,一个政策。风格指南本身应尽可能简洁明了。长风格指南会被所有人忽视(除了寻找责任的管理层),所以只需写“做”或“不要”,并在其他地方说明您对违反指南的行为(例如在公司政策中)同行评审的方式)。

    【讨论】:

    • ...但是 foo 方法本身不是 const 。
    • @Wolf:是的,const member function
    • 我知道,这是它的name,但是成员函数的常量和数据的常量不同。
    • @Wolf:但重点仍然存在:您可以随时将const 说明符放在它所指内容的右侧,但您可以将它放在left仅在少数特殊情况下。
    • 当然,模式const <type> <whatever> 似乎在数据声明中非常常见。我用了这么久,但现在我真的考虑重新考虑一下。
    【解决方案7】:

    虽然const intint const 之间没有有意义的区别(我已经看到这两种样式都在使用),但const int *int * const 之间还是有区别的。

    首先,您有一个指向 const int 的指针。您可以更改指针,但不能更改它指向的值。在第二个中,您有一个指向 int 的 const 指针。您无法更改指针(希望根据您的喜好对其进行初始化),但您可以更改指向 int 的值。

    正确的比较是 const int *int const *,它们都是指向 const int 的指针。

    请记住,* 不一定能如您所愿。声明 int x, y; 将按预期工作,但 int* x, y; 声明了一个指向 int 的指针和一个 int。

    【讨论】:

      【解决方案8】:

      一旦您训练自己从右到左阅读您的 C++ 类型声明,将“const”放在类型声明之后会更有意义。

      我会以 0 换 3 的价格锁定你的牛人 :)

      【讨论】:

      • ...但不是从左到右阅读,这是正常情况,例如 const int N = 26 可以被阅读为 常量整数 N 的值为 26.
      • @Wolf:而int const N = 26 可以读作整数常量 N 的值为 26。
      • @DevSolar,是的,这绝对是真的(我发现,抱歉我的英语很差)。感谢您再次澄清:)
      【解决方案9】:

      就我个人而言(这是个人偏好),我发现从右到左阅读类型声明最容易。尤其是当您开始将引用混入其中时。

      std::string const&  name = plop; // reference to const string.
      
      const std::string&  flame =plop; // reference to string const;
                                       // That works better if you are German I suppose :-)
      

      【讨论】:

      • 当然,你也可以说const std::string&,从左到右读起来很好。
      • 我的同事碰巧懂德语,所以这可能是问题的一部分。 :-)
      • +1 说德语的人应该考虑将 'int const' 读作 integer constant 这样可以避免痛苦。
      【解决方案10】:

      这里的最终问题是,他认为将 const 放在 int 之后是如此丑陋,而且对可读性如此有害,以至于应该在样式指南中禁止它

      真的吗?

      给我展示一个看到就会陷入困境的程序员:

      int foo() {
      }
      

      对比

      int foo()
      {
      }
      

      ...我会向你展示一个对细节不够关注的程序员。

      没有一个称职的专业程序员会在风格上存在表面差异的问题。

      编辑:确实 const int* 和 int* const 的意思并不完全相同,但这不是重点。 OP 的同事提出的观点是,风格的差异使代码难以理解和维护。我不同意这种说法。

      【讨论】:

      • 给我看一个认为这些意思相同的程序员,我给你看一个对细节不够关注的程序员。
      • 没错,它们的意思不同,但这不是论点。争论是风格上的差异“对可读性非常有害”,应该被取缔。如果您不喜欢 const int* 与 int* const 参数,那很好。我们可以将谈话扩展到花括号。我的主张是,没有优秀的程序员会因为风格差异而在理解代码时遇到问题。所以不要为小事出汗。
      • 其实根本区别是"const int*" vs. "int const *"
      【解决方案11】:

      让我把我的 2 美分投入讨论。

      在现代 C++ 中,我建议坚持使用 int const 而不是 const int 以突出 constconstexpr 之间的区别。这将有助于程序员记住 const int 不能总是被认为是编译时常量,因为:

      • 编译器可能(会?)为其分配内存,
      • 此内存甚至可能没有写保护(堆栈段),
      • 这样的常量不能服务于all the duties constexpr can

      【讨论】:

        【解决方案12】:

        规则很好遵守。更简单的规则更好。 const 在什么是 const 的右边。

        接受这份声明:

        int 主要的 ( int 常量 argc , 字符常量 * 常量 * 常量 argv ) ...

        【讨论】:

          【解决方案13】:

          我同意你们两个。您应该将 const 放在类型之后。我还发现看着它是一种必须摧毁的可憎之物。但我最近对const value parameters 的奇迹的尝试让我明白了为什么将 const 放在第二位是有意义的。

          int *
          int const *
          int * const
          int const * const
          

          光是看着,我脖子上的汗毛都竖起来了。我敢肯定这会让我的同事感到困惑。

          编辑:我只是想知道在课堂上使用它:

          class Foo {
              Bar* const bar;
              Foo(const Foo&) = delete; // would cause too many headaches
          public:
              Foo() : bar(new Bar) {}
              ~Foo() { delete bar; }
          };
          

          此示例中的bar 在功能上等同于Bar&,但它位于堆上并且可以删除。对于每个 Foo 的生命周期,将有一个与之关联的 Bar。

          【讨论】:

            【解决方案14】:

            我喜欢使用下面的表格来声明"manifest constants"。在这种情况下,值本身是一个常量,所以我将“const”放在首位 (same as Bjarne) 以强调常量应该在编译时体现出来,并且可以用于编译器的特定优化。

            const int i = 123;
            

            为了声明不会用于修改值的引用,我使用以下形式强调标识符是“常量引用”这一事实。引用的值可能是也可能不是常数。 [相关讨论:如果函数参数不是指针或引用,你甚至会使用“const”吗? Use of 'const' for function parameters]

            void fn( int const & i );
            

            对于指针,我使用与引用相同的形式,原因基本相同(尽管术语“常量指针”似乎比“常量引用”更含糊)。

            void fn( int const * i );
            

            另外,正如另一位发帖者所指出的,当您有多个间接级别时,这种形式保持一致。

            void fn( int const * const * i );
            

            在我使用 C++ 的经验中,您声明一个常量指针的最后一种情况非常罕见。无论如何,你在这里真的没有任何选择。 [这个案例表明,最一致的方法是将“const”这个词放在类型之后——因为事实上,对于这个特定的声明来说,必需。 ]

            void fn( int * const i );
            

            ...除非你使用 typedef。

            typedef int * IntPtr;
            
            void fn1( const IntPtr i );
            void fn2( IntPtr const i );
            

            最后一点:除非您在低级域中工作,否则大多数 C++ 代码应该从不声明指针。因此,这个讨论的那个方面可能与 C 更相关。

            【讨论】:

            • 这完全不是真的!指针有很多用途。例如,指向局部变量选择的指针。
            • @rlbond:我不确定我是否理解你的例子,但我猜参考资料会适用。
            • @rlbond:你说得对,指针有很多用途。只是 C++ 为您提供了更好(更安全)的方法来完成 C 中指针所做的事情。C++ 是一种多范式语言,并且使用低级(基于指针)范式对于某些目的很重要,但在大多数情况下,您最好尽量减少原始指针的使用以支持更高级别的抽象。
            • This question 涉及合法性和解释。该页面上包含一个非常有趣的评论,值得重复:“一个经验法则可以帮助您记住如何发现 'const' 是否适用于指针或指向的数据:在星号处拆分语句,那么,如果const关键字出现在left部分它属于指向数据 -- 如果它在right部分关于指针”。此分析也适用于 C 的 restrict
            【解决方案15】:

            在现代 C++11 中,您还可以使用模板类型定义来增加复杂声明的清晰度:

            template<typename T>
            using Const = const T;
            
            template<typename T>
            using Ptr = T*;
            

            您在问题中提到的三个声明:

            int const * a;
            int * const b;
            int * const * c;
            

            然后写成:

            Ptr<Const<int>> a;
            Const<Ptr<int>> b;
            Ptr<Const<Ptr<int>>> c;
            

            【讨论】:

            • 可能是因为该功能是新功能,但我发现这并不容易破译。假设某处有错误。与中间的三行相比,要说服自己最后一行的声明是正确的,似乎需要做很多工作。
            • 我想。看起来我还是坚持“const 向右”规则而不需要那些额外的声明会好得多。
            • 我正在投票赞成这个答案(因为我认为这很酷,而且我很高兴你提到它),即使语法让我的大脑受伤并且我永远不会使用它.
            猜你喜欢
            • 2015-07-16
            • 1970-01-01
            • 2013-04-14
            • 1970-01-01
            • 2021-05-10
            • 2010-11-11
            相关资源
            最近更新 更多