将类似C ++的const引入语言有什么问题?


17

我对C ++的想法感兴趣,而const不是特定的执行(例如Castingaway const)。

以C#为例-它缺少类似于C ++的const,其原因是通常的-人员和时间。在这里,另外,似乎C#团队研究了C ++的const市场营销CLR 执行情况,并且在这一点上已经足够了(请参阅为什么在c#和const参数中没有const成员方法;谢谢Svick)。如果我们走得更远,还有其他吗?

还有更深的东西吗?以多重继承为例-通常(对于用户)它很难理解,因此没有添加到语言中(例如钻石问题)。

在《自然》杂志中是否存在某种const问题,那就是让语言最好避免这种问题?像确定const是深还是浅之类的东西(有了const容器,这意味着我也不能添加新元素或更改现有元素;如果元素属于引用类型怎么办)?

更新:虽然我提到C#,因此从历史的角度出发,但我对const语言潜在问题的性质感兴趣。

这不是语言的挑战。请忽略当前趋势或受欢迎程度等因素-我只对技术问题感兴趣-谢谢。


3
顺便说一句:多重继承可能不难掌握,但是方法解析可能变得很丑陋。天真的深度优先分辨率很快变得不直观(“ 钻石问题 ”)。该C3方法解析顺序(96年发表)解决了这一点,但肯定是容易理解。因此,将多重继承取为非法通常看起来很明智-但真正的问题是Java早于C3算法,而C#却在Java之后面向自身。
阿蒙2014年

3
@amon很多人并不真正认为多重继承是您首先要拥有的功能,例如D编程语言的创建者:这是值得商 bat 的复杂功能。以高效的方式实现它非常困难,并且编译器在实现它时容易出现许多错误。MI的几乎所有值都可以通过单继承,接口和聚合来处理。剩下的不足以证明MI实现的重要性。(来源)
jcora 2014年


2
即使没有多重继承,你可以已经编码任何3-SAT问题,方法解析(或更精确地重载解析)在C#中,从而使得NP完全问题。
约尔格W¯¯米塔格

1
@svick,非常感谢。由于此问题我没有任何答案,因此我自由地进行了大量编辑,着重于问题的本质,而不是历史原因,因为似乎其中99%是“时间+人+反C ++偏见” + CLR”。
greenoldman 2014年

Answers:


10

请注意,这是否符合您的条件,但是在标准ML等功能语言中,默认情况下所有内容都是不可变的。通过通用错误ref类型支持突变。因此,int变量是不可变的,并且ref int变量是ints 的可变容器。从本质上讲,变量是数学意义上的变量(未知但固定值),而refs是命令式编程意义上的“变量”-可以读写的存储单元。(我喜欢称它们为可分配对象。)

我认为问题有const两个方面。首先,C ++缺少垃圾回收,这对于拥有非平凡的持久数据结构是必需的。const 必须有足够的深度才能具有任何意义,但是在C ++中具有完全不变的值是不切实际的。

其次,在C ++中,您需要选择加入const而不是选择退出。但是,当您忘记const某些内容并随后对其进行修复时,您将最终遇到@RobY答案中提到的“常量中毒”情况,其中const更改将遍历整个代码。如果const是默认设置,则不会发现自己具有const追溯力。另外,必须在const任何地方添加都会增加代码的噪音。

我怀疑随后出现的主流语言(例如Java)在很大程度上受到C和C ++的成功以及思维方式的影响。举例来说,即使使用垃圾回收,大多数语言的回收API也会采用可变的数据结构。一切都是可变的并且不变性被视为一个极端案例,这一事实充分说明了流行语言背后的命令式思维。

编辑:在考虑了greenoldman的评论之后,我意识到这const不直接涉及数据的不变性;const编码为方法的类型是否对实例有副作用。

可以使用突变来实现参照透明的行为。假设您有一个函数,当该函数连续被调用时返回不同的值-例如,一个函数从中读取单个字符stdin。我们可以使用缓存/存储此函数的结果来生成参考透明的值流。流将是一个链表,该链表的节点将在您第一次尝试检索其值时调用该函数,然后将其缓存。所以,如果stdinconstains Hello, world!,你第一次尝试检索第一个节点的值,它会读取一个char和回报H。之后,它将继续返回,H而无需进一步调用以读取char。同样,第二个节点char将从stdin第一次尝试检索其值时,这次将返回e并缓存该结果。

有趣的是,您已经将本来有状态的过程变成了看似无状态的对象。但是,必须改变对象的内部状态(通过缓存结果)以实现此目的-突变是一种良性效果CharStream const即使流的行为就像一个不可变的值,也无法使我们成为现实。现在,假设有一个方法Stream接口const,而您的所有功能都在期望中const Streams。您CharStream无法实现该接口!

编辑2:显然有一个名为C ++的关键字mutable,它使我们可以作弊CharStream const。但是,此漏洞破坏了const'保证'-现在您真的不能确定某些东西不会通过其const方法发生变化。我想不是不好,因为您必须明确要求漏洞,但是您仍然完全依赖荣誉系统。)

其次,假设您具有高阶函数-也就是说,您可以将函数作为参数传递给其他函数。constness是函数签名的一部分,因此您将无法将非const函数作为参数传递给需要函数的const函数。const在此处盲目执行将导致失去普遍性。

最后,操作const对象并不能保证它不会使您背后的某些外部(静态或全局)状态发生变化,因此const的保证并不像它们最初出现的那样强大。

对我来说,尚不清楚将副作用的存在或不存在编码到类型系统中通常是一件好事。


1
+1谢谢。不可变对象是C ++ const以外的东西(它更多是视图)。但是说,C ++默认情况下会具有const,并且var按需只会留下一个问题-深度?
greenoldman 2014年

1
@greenoldman好点。自从我使用C ++已经有一段时间了,所以我忘记了您const可以引用一个非const对象。即使这样,我也不认为拥有浅浅的表述毫无意义const。整个要点const是保证您将无法通过该引用修改对象。不允许您const通过创建非const引用来规避,那么为什么可以const通过对对象成员进行变异来规避呢?
2014年

+1 :-)您的更新中非常有趣的问题(使用非/常量lambda生存,以及外部状态较弱的问题),我必须消化更长的时间。无论如何,至于您的评论,我没有什么邪恶的想法-恰恰相反,我正在寻找功能以提高代码的清晰度(另一个是:非空指针)。这是同一类别(至少是我的目的)-我定义了func foo(const String &s),这是s不会更改的信号foo
greenoldman 2014年

1
@greenoldman我绝对可以看到它的吸引力和理由。不过,我不认为有一个真正的解决不是避免可变状态尽可能其他问题(连同像其他令人讨厌的事情null和继承。)
Doval

8

主要问题是程序员往往没有充分使用它,因此当他们碰到需要它的地方时,或者维护者后来想修复const正确性时,会产生巨大的连锁反应。如果没有const,您仍然可以编写完美的不可变代码,您的编译器将不会强制执行它,并且优化起来会更加困难。有些人喜欢他们的编译器不帮他们。我不了解那些人,但其中有很多人。

在函数式语言中,几乎所有内容都是const,并且可变的是罕见的例外(如果允许的话)。这具有几个优点,例如更容易的并发性和更容易的关于共享状态的推理,但是如果您来自具有可变文化的语言,则需要一定的习惯。换句话说,不匮乏const是人的问题,而不是技术问题。


+1谢谢。虽然不可变对象不是我想要的东西(您可以使对象在C#或Java中不可变),但是即使使用不可变方法,您也必须确定对象是深还是浅,例如使用指针/引用容器(可以吗?更改要指向的对象的值)。一天之后,似乎主要的问题是设置为默认值,并且在设计新语言时很容易解决。
greenoldman 2014年

1
为lolz +1 @“有些人更喜欢他们的编译器对他们没有帮助。我听不懂这些人,但其中有很多人。”
Thomas Eding

6

我认为缺点是:

  • 当const的一个声明可以强制在先声明或后声明使用const时,就会导致“ const中毒”的问题,而这可能导致其后继声明的前一个声明使用const等。您无法控制自己,那么您可能会陷入困境。

  • 这不是绝对的保证。通常在某些情况下,const仅保护引用,但由于某种原因而无法保护基础值。即使在C语言中,也存在const无法到达的极端情况,这削弱了const提供的承诺。

  • 通常,业界的观点似乎正在远离强大的编译时保证,无论它们可能给您和我带来多大的安慰。因此,由于const中毒,我花了整个下午花了我66%的代码库,一些Python guy已经敲出了10个完美可行的,设计良好,经过测试的,功能齐全的Python模块。当他这样做的时候,他甚至没有黑客。

因此,也许的问题是,当您试图采用流行的新语言时,为什么还要发扬甚至是一些专家也有争议的潜在争议功能?

编辑:简短的回答,一种新的语言有一个陡峭的山坡,可以被采用。设计师真的需要认真考虑需要采用哪些功能。以Go为例。Google通过做出一些Conan-the-Barbarian风格的设计选择,残酷地截断了一些最深层次的宗教斗争,从我所看到的情况来看,我实际上认为该语言是更好的选择。


2
您的第二项不正确。在C ++中,有三种/四种方式来声明引用/指针wrt。constness:int *可变值的int const *可变指针,const值的int * const可变指针,可变值的int const * constconst指针,const值的const指针。
菲涅耳2014年

谢谢。我问的是问题的本质,而不是市场营销的问题(“离开行业”不是C ++的本质const),因此我认为这样的“原因”无关紧要(至少对于这个问题而言)。关于const中毒-您能举个例子吗?因为AFAIK是优势,所以您传递一些东西const,它应该保留const
greenoldman 2014年

1
这不是const问题,而是真正地更改类型-无论您做什么,都将始终是一个问题。考虑通过RichString,然后实现更好的通过String
greenoldman 2014年

4
@RobY:我不确定您要针对后者前者。但是:在下午遍历代码后,您应该已经学会了应该自下而上而不是自上而下地重构const正确性,即从最里面的单元开始,然后逐步向上。也许您的单位还不够孤立,而是紧密耦合,或者您已沦为内部系统的猎物。
菲涅尔2014年

7
“常量中毒”并不是真正的问题-真正的问题是C ++默认为non- const。不const应该做的事情太容易了,const因为您必须选择加入而不是退出。
2014年

0

添加const语言时存在一些哲学问题。如果声明const一个类,则意味着该类不能更改。但是类是一组与方法(或变异子)耦合的变量。我们通过将类的状态隐藏在一组方法后面来实现抽象。如果一个类被设计为隐藏状态,那么您需要更改器从外部更改该状态,然后const不再更改。因此,只能声明const为不可变的类。因此,const似乎仅在类(或要声明的类型const)很简单的情况下才有可能...

所以我们const还是需要吗?我不这么认为。我认为,const如果您有一个好的设计,就可以轻松做到。您需要什么数据,在哪里,什么方法是数据到数据的方法,以及需要用于I / O,网络,视图等的抽象。然后,您自然地拆分了处理状态的模块和类,并且有了方法和方法。不需要状态的不可变类。

我认为大多数问题都来自大型胖数据对象,这些对象可以保存,变换和绘制自身,然后将其传递给渲染器。因此,您无法复制数据的内容,因为这对于大数据对象而言太昂贵了。但是您也不希望它改变,所以您需要像const您想的那样。让编译器找出您的设计缺陷。但是,如果仅将这些对象拆分为一个不可变的数据对象,并在负责的模块中实现方法,则可以(仅)传递指针并执行要对数据进行的操作,同时还可以保证不可变性。

我知道在C ++中可以声明某些方法const,而不必在其他方法上声明,因为它们是变异器。然后一段时间后,您需要一个缓存,然后一个getter改变一个对象(因为它是延迟加载的)而const不再存在。但这就是抽象的全部意思了吗?您不知道每次调用都会改变什么状态。


1
“所以const似乎仅在类(或要声明const的类型)很琐碎的情况下才有可能……” 持久性数据结构是不可变的且不平凡的。您几乎不会在C ++中看到它们,因为它们几乎都在多个实例之间共享其内部数据,并且C ++缺少垃圾收集来使它们正常工作。“我认为,如果您有良好的设计,就可以轻松地不用const。” 不变的值使代码更容易推理。
2014年

+1谢谢。您能否在“将常量声明为类”上进行澄清?您的意思是将class设置为const吗?如果是的话,我不是在问这个问题-C ++ const的工作方式类似于视图,您可以在对象而不是类(或在C ++ 11中有所更改)旁边声明一个const。下一段和“轻松”一词也有问题-对于C#中的C ++-const有一个很好的问题,并且涉及的工作量是至关重要的-对于您要放入的每种类型const(在C ++的意义上),您还必须为所有属性定义接口。
greenoldman 2014年

1
当您谈论“班级”时,您实际上是指“班级实例”,对吗?我认为这是一个重要的区别。
2013年
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.