在const之前还是const之后?


145

首先,您可能知道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应用于关键字的左侧(默认情况下是?),我猜他们认为添加“快捷方式”以其他方式应用关键字和类型限定符至少在声明更改之前一直没有危害。解析*或&...

我也假设在C中也是这种情况?


9
在宏中,总是const 类型之后添加,例如#define MAKE_CONST(T) T const而不是,#define MAKE_CONST(T) const T这样MAKE_CONST(int *)它将正确扩展为int * const而不是const int *
jotik 2015年

6
我已经看到了这两种样式,分别称为“东部常量”和“西部常量”。
Tom Anderson

13
@TomAnderson,但实际上它应该是“ east const”和“ const west”。
YSC

Answers:


96

为什么有两种正确的方式来指定const数据?在什么情况下,相对于其他情况,您更喜欢还是需要一种?

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

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

因为如果 const限定符出现在类型说明符之前或之后,所以无论哪种方式都可以接受它。

声明函数指针时会发生类似的情况,其中:

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

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

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


7
Kernighan是该书的合著者,但没有参与C的设计,只是Ritchie。
Tom Zych

8
我永远不记得哪个是哪个。感谢您的解释,我终于有了mnemotechnic来记住它。谢谢!在*编译器解析器不知道它是指针之前,因此它是数据值的const。*之后与常量指针有关。辉煌。最后解释了为什么我能做的const char很好char const
Vit Bernatik

2
对为什么以这种方式进行的猜测在我看来是微弱的/自相矛盾的。也就是说,如果我正在定义一种语言并编写一个编译器,并且我想使其保持简单,并且“从左到右解析输入,并在使用完每个标记后就对其进行处理”,就像我说的那样我要求const总是在符合条件的东西之后...正是这样,这样我就可以在消耗完const之后立即立即完成对const的处理。因此,这似乎是禁止而不是允许使用 west-const的理由。
唐·哈奇

3
“因为如果const限定符出现在类型说明符之前或之后,语义不会改变,所以无论哪种方式都可以接受它。” 那不是循环推理吗?问题是为什么这样定义语义,所以我认为这句话没有任何作用。
唐·哈奇

1
@donhatch您必须记住,相对于今天和我们基于对良好的编程语言设计的了解而做出的假设,当时的语言是相当新的事物。同样,一个人的语言是宽容的还是受限制的也是一种价值判断。例如,python应该有一个++运算符吗?恕我直言,“这句话”使我意识到,除了他们可以的以外,没有其他特殊原因。也许他们今天会做出不同的选择,也许不是。
ragerdl

75

规则是:

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

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

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


18
我更喜欢将其放在左侧,但我认为将其放在右侧更有意义。您通常从右到左读取C ++中的类型,例如,Object const *是指向const Object的指针。如果将其const放在左侧,它将被读为const对象的指针,该对象的流动性不是很好。
Collin Dauphinee

1
我给人的印象是,左侧是为了与其他类型的C声明保持人文风格的一致性(在计算机方面,这不是正确的,因为const它不是存储类,但是人不是解析器)。
geekosaur 2011年

1
@Heath我认为这比规则更重要,我经常听到它是一种记住编译器将如何解释它的方式...我了解它是如何工作的,所以我只是对它背后的思考过程感到好奇决定支持这两种方式。
2011年

3
@HeathHunnicutt规则存在,但稍微复杂一点:c-faq.com/decl/spiral.anderson.html
imallett

2
@HeathHunnicutt:螺旋规则是第一个评论者评论的扩展版本,“您通常从右到左阅读[C /] C ++中的类型”。我以为你在与这个矛盾。但是,我认为您可能反而是在指答案本身。
imallett 2014年

56

我更喜欢第二种语法。通过从右到左读取类型声明,它可以帮助我跟踪“什么”是常量:

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

3
究竟。Object const* const不是“指向常量对象的常量指针” const const Object*。除非在很多人绝对喜欢它的特殊情况下,否则“ const”不能在左边。(请参阅上方的希思。)
cdunn2001'1

38

声明中关键字的顺序并不确定。“一个真实的订单”有多种选择。像这样

int long const long unsigned volatile i = 0;

还是应该

volatile unsigned long long int const i = 0;

??


25
+1代表一个简单变量的完全混乱的定义。:)
Xeo

@rubenvb-是的,不幸的是。语法只是说a decl-specifier-seqdecl-specifiers 的序列。语法没有给出顺序,每个关键字的出现次数仅受某些语义规则的限制(您可以有一个,const但可以只有两个long:-)
Bo Persson

3
@rubenvb-是的,unsigned是一种类型,与unsigned int和相同int unsignedunsigned long是另一种类型,与unsigned long int和相同int long unsigned。看到图案了吗?
Bo Persson

2
@Bo:我看到了混乱,必须要有三个才能看到一个模式;)。好,谢谢
rubenvb

1
您曾经能够添加static一些单词,但是直到最近才有编译器抱怨static需要首先出现。
Mark Lakata 2015年

8

第一条规则是使用本地编码标准要求的任何格式。之后,const当涉及到typedef时,将in放在前面不会引起混乱,例如:

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

如果您的编码标准允许使用typedef指针,那么它实际上应该坚持将const放在类型之后。在每种情况下(但将const应用于类型时),都必须遵循其所适用的内容,因此一致性也为之后的const辩护。但是本地编码准则胜过所有这些。通常,差异并不重要,不足以返回并更改所有现有代码。


我认为这可能突出了在这家商店中,在我们定义较松散的标准中没有指针的typedef 的原因。
2011年

1
我的策略(当我一个人决定时)是将const放在后面(为了保持一致)而不要使用typedef指向指针(或者通常使用typedef :)。顺便说一句,string :: iterator与string :: const_iterator可能也应该纳入您的决策范围。(只是使事情混乱:-)。没有正确的答案。)
James Kanze 2011年

嗯,是的,我本可以const std::string::const_iterator很好地考虑其行为;)
AJG85 2011年

2
@JamesKanze —请稍等,在这里帮助我...在发布的示例中我看不到混乱。const IntPtr p1除了“常量整数指针”(即“常量整数指针”)以外,还有什么可能意味着什么?即使他们不知道如何IntPtr定义,也没有人会认为这p1是可变的。因此,为什么有人会错误地认为这*p1是不变的呢?而且,将const放在其他任何地方(例如IntPtr const p1),根本不会改变语义。
托德·雷曼

3
@ToddLehman您可能看不到混乱,但是大多数C ++程序员却确实看到了,并且系统地将其弄错了(毫无疑问,诸如std::vector<T>::const_iterator,它不是const的迭代器,而是它所指向的东西的帮助)。
James Kanze 2014年

7

有历史原因可以接受左或右。Stroustrup到1983年在C ++中添加了const,但直到C89 / C90才将其添加到C中。

在C ++中,有一个很好的理由总是在右边使用const。您将在所有地方保持一致,因为const成员函数必须以这种方式声明:

int getInt() const;

1
...嗯,“好的理由”并没有那么令人信服,因为“ const”的其他可能位置并不意味着同一件事。 const int& getInt(); int& const getInt();
Maestro

1
@Maestro:我的建议int const& getInt();是比同等更好const int& getInt();,而int& const getInt();你与比较是多余的(引用已经常数)虽然合法,通常会给出警告。无论如何,成员函数上的const会将函数中的this指针从更改Foo* constFoo const* const
尼克·韦斯特盖特

const成员函数上的含义并不完全相同,或者是void set(int)&;对函数的某种引用?
戴维斯·鲱鱼
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.