【问题标题】:Const before or const after?const 之前还是 const 之后?
【发布时间】:2011-07-27 01:51:43
【问题描述】:

首先,您可能知道const 可用于使对象的数据或指针不可修改或两者兼而有之。

const Object* obj; // can't change data
Object* const obj; // can't change pointer
const Object* const obj; // can't change data or pointer

不过你也可以使用语法:

Object const *obj; // same as const Object* obj;

似乎唯一重要的是您将const 关键字放在星号的哪一侧。就我个人而言,我更喜欢将 const 放在类型的左侧以指定它的数据不可修改,因为我发现它在我的从左到右的思维方式中读起来更好,但哪个语法先出现?

更重要的是,为什么有两种正确的方法来指定const 数据?在什么情况下,如果有的话,您更喜欢或需要其中一种?

编辑:

所以在我出生之前很久就起草了编译器应该如何解释事物的标准时,这听起来像是一个武断的决定。由于const 应用于关键字左侧的内容(默认情况下?)我猜他们认为添加 "shortcuts" 以以其他方式应用关键字和类型限定符没有害处至少直到声明通过解析 * 或 & 发生更改...

C 中也是这种情况,然后我假设?

【问题讨论】:

  • 在宏中总是添加const 在类型之后,例如#define MAKE_CONST(T) T const 而不是 #define MAKE_CONST(T) const T 这样MAKE_CONST(int *) 将正确扩展为 int * const 而不是 const int *。
  • 我见过这两种样式被称为“east const”和“west const”。
  • @TomAnderson 但实际上应该是“east const”和“const west”。

标签: c++ c syntax constants


【解决方案1】:

C 使用从右到左的语法。只需从右到左阅读声明:

int var = 0;

// one is a pointer to a const int
int const * one = &var; 
// two is a pointer to an int const (same as "const int")
const int * two = &var; 

// three is a constant pointer to an int
int * const three = &var;

留给“const”的第一件事会受到它的影响。

如需更多乐趣,请阅读本指南: http://cseweb.ucsd.edu/~ricko/rt_lt.rule.html

【讨论】:

    【解决方案2】:

    为什么有两种正确的方法来指定const 数据?在什么情况下,如果有的话,您更喜欢或需要其中一种?

    基本上,const 在说明符中星号之前的位置无关紧要的原因是 C 语法是由 Kernighan 和 Ritchie 定义的。

    他们以这种方式定义语法的原因可能是他们的 C 编译器从左到右解析输入,并在每个标记消耗它时完成处理。使用* 标记会将当前声明的状态更改为指针类型。在* 之后遇到const 意味着const 限定符应用于指针声明;在* 之前遇到它意味着限定符应用于指向的数据。

    因为如果const 限定符出现在类型说明符之前或之后,语义不会改变,因此无论哪种方式都可以接受。

    在声明函数指针时会出现类似的情况,其中:

    • void * function1(void) 声明了一个返回 void * 的函数,

    • void (* function2)(void) 声明一个 函数指针 指向返回 void 的函数。

    再次需要注意的是语言语法支持从左到右的解析器。

    【讨论】:

    • Kernighan 与人合着了这本书,但没有参与 C 的设计,只是 Ritchie。
    • 我永远不记得哪个是哪个。感谢您的解释,我终于有了助记符来记住它。谢谢!在* 编译器解析器不知道它是指针之前,它是数据值的 const。 *后与常量指针有关。杰出的。最后它解释了为什么我可以做到const char 和char const。
    • 对我为什么这样做的猜测似乎相当弱/自相矛盾。也就是说,如果我正在定义一种语言并编写一个编译器,并且我想保持简单并“从左到右解析输入并在每个令牌使用它时完成处理”,正如你所说,在我看来我会要求 const 总是在它符合条件的东西之后出现......正是这样我总是可以在我消费它后立即完成处理 const 。所以这似乎是禁止 west-const的论据,而不是允许它。
    • “因为如果 const 限定符出现在类型说明符之前或之后,语义不会改变,因此无论哪种方式都可以接受。”这不是循环推理吗?问题是为什么语义是这样定义的,所以我认为这句话没有任何贡献。
    • @donhatch 你必须记住,相对于今天以及我们基于对良好编程语言设计的熟悉度所做的假设,语言在当时还是相当新的事物。此外,一个人是否有一种允许或限制的语言是一种价值判断。例如,python 是否应该有 ++ 运算符?恕我直言,“这句话”帮助我意识到除了他们可以之外没有任何特别的原因。也许他们今天会做出不同的选择/也许不会。
    【解决方案3】:

    有历史原因,左或右都可以接受。 Stroustrup had added const to C++ by 1983,但直到 C89/C90 才进入 C。

    在 C++ 中,始终在右侧使用 const 是有充分理由的。您将在任何地方保持一致,因为 const 成员函数必须以这种方式声明:

    int getInt() const;
    

    【讨论】:

    • ...好吧,“好理由”并没有那么令人信服,因为“const”的其他可能位置并不意味着同样的事情。 const int& getInt();int& const getInt();
    • @Maestro:我建议 int const& getInt(); 比等效的 const int& getInt(); 更好,而您与之比较的 int& const getInt(); 是多余的(引用已经是 const)虽然合法并且通常会发出警告.无论如何,成员函数上的 const 会将函数中的 this 指针从 Foo* const 更改为 Foo const* const。
    • const 在成员函数上的含义完全不同——或者void set(int)&; 是对函数的某种引用?
    【解决方案4】:

    声明中关键字的顺序并不是完全固定的。 “唯一真正的秩序”有许多替代方案。像这样

    int long const long unsigned volatile i = 0;
    

    应该是这样

    volatile unsigned long long int const i = 0;
    

    ??

    【讨论】:

    • +1 表示一个简单变量的完全混乱的定义。 :)
    • @rubenvb - 是的,unsigned 是一种类型,与 unsigned int 和 int unsigned 相同。 unsigned long 是另一种类型,与unsigned long int 和int long unsigned 相同。看到图案了吗?
    • @Bo:我看到了混乱,必须有三个才能看到模式;)。好的,谢谢
    • 您过去可以将static 添加到混乱的单词中,但直到最近才有编译器抱怨static 需要放在第一位。
    • @rubenvb 他的意思是unsigned 本身只是unsigned int 的简写(如果不清楚的话),同样short 本身与@987654335 相同@。 (虽然要小心,使用char 和unsigned/signed char 是有区别的...它不是自己的类型,但它有自己的含义,因为char 可以在内部签名或未签名,并且哪个并不重要,但基本上尽量保持一致,对于标准库函数,只需使用 plain char 并让它使用自己的内部表示。)
    【解决方案5】:

    我更喜欢第二种语法。它可以帮助我通过从右到左阅读类型声明来跟踪“什么”是不变的:

    Object * const obj;        // read right-to-left:  const pointer to Object
    Object const * obj;        // read right-to-left:  pointer to const Object
    Object const * const obj;  // read right-to-left:  const pointer to const Object
    

    【讨论】:

    • 没错。 “指向常量对象的常量指针”是Object const* const,而不是const const Object*。 "const" 不能在左边,除非有这么多人绝对喜欢它的特殊情况。 (见上面的希思。)
    【解决方案6】:

    第一条规则是使用本地编码标准的任何格式 需要。之后:将const 放在前面导致无穷无尽 涉及 typedef 时的混淆,例如:

    typedef int* IntPtr;
    const IntPtr p1;   // same as int* const p1;
    

    如果你的编码标准允许 typedef 的指针,那么它真的 应该坚持将 const 放在类型之后。在任何情况下,但 当应用于类型时, const 必须遵循它所应用的内容,所以 coherence 也支持 const after。但是本地编码 指导方针胜过所有这些;差异通常并不重要 足以返回并更改所有现有代码。

    【讨论】:

    • 我认为这可能突出了我们在这家商店的定义相当松散的标准中没有指针类型定义的原因。
    • 我的策略(当我独自决定时)是把 const 放在(为了连贯性)和之后不要使用 typedefs 到指针(或 typedefs 在一般的):-)。顺便说一句,string::iterator 与 string::const_iterator 可能也应该考虑到您的决定中。 (只是为了混淆事物:-)。没有正确答案。)
    • 啊,是的,我本可以将const std::string::const_iterator 的行为也包括在内,以作好衡量;)
    • @JamesKanze — 等一下,帮帮我……我在发布的示例中看不到混淆。除了“常数整数指针”(即“指向整数的常数指针”)之外,const IntPtr p1 还可能意味着什么?即使不知道IntPtr 是如何定义的,头脑正常的人也不会认为p1 是可变的。就此而言,为什么有人会错误地认为*p1 是不可变的?更重要的是,将 const 放在其他任何地方(例如,IntPtr const p1),根本不会改变语义。
    • @ToddLehman 你可能看不到混淆,但大多数 C++ 程序员都会这样做,并且系统地弄错了(毫无疑问,std::vector<T>::const_iterator 之类的东西有所帮助,它不是 const 的迭代器,但它指的是什么)。
    【解决方案7】:

    规则是:

    const 适用于它剩下的东西。如果左边什么都没有,那么它适用于它右边的东西。

    我更喜欢在事物的右侧使用 const 来作为 const,因为它是 const 定义的“原始”方式。

    但我认为这是一个非常主观的观点。

    【讨论】:

    • 我更喜欢把它放在左边,但我认为把它放在右边更有意义。您通常从右到左读取 C++ 中的类型,例如 Object const * 是指向 const 对象的指针。如果你把 const 放在左边,它会读作指向一个 const 对象的指针,它的流动性不是很好。
    • 我的印象是左边是人类风格与其他类型的 C 声明的一致性(计算机方面它不正确,因为 const 不是存储类,但是人们不是解析器)。
    • @Heath 我相信这更像是一个指导方针而不是规则,我经常听到它作为记住编译器如何解释它的一种方式......我理解它是如何工作的,所以我是只是对双向支持的决定背后的思考过程感到好奇。
    • @HeathHunnicutt 规则存在,但稍微复杂一点:c-faq.com/decl/spiral.anderson.html
    • @HeathHunnicutt:螺旋规则是第一个评论者评论“您通常从右到左阅读 [C/]C++ 中的类型”的扩展版本。我以为你在反驳这一点。但是,我认为您可能指的是答案本身。
    猜你喜欢
    • 1970-01-01
    • 2014-01-06
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 2020-09-27
    • 1970-01-01
    • 2013-04-23
    相关资源
    最近更新 更多