为什么scala没有C ++ - 就像const-semantics?

时间:2010-10-05 10:33:43

标签: c++ scala types const

在C ++中。我可以将大多数事物声明为常量,例如:
变量:const int i=5;
Scala有val i=5,但这只会阻止重新分配,而不会更改对象,如下例所示:
C ++:

const int i[]={1,2,3,4};
i[2]=5; //error
Scala:
val a=Array(1,2,3,4)
a(2)=5 //a is now Array(1, 2, 5, 4)

成员函数变得更糟:
C ++:

class Foo {
int i;
int iPlusFive() const {return i+5;}
int incrementI(){ return ++i; }
}
我可以肯定,调用iPlusFive不会改变对象,也不会意外地在const对象上调用incrementI。

当涉及到集合时,C ++继续使用const集合的const-correct条纹:简单地将你的向量声明为const而你无法改变它。将non-const vector<Int>分配给const vector<Int>,编译器不会复制任何内容,并且会阻止您更改现在const集合中的任何内容。

Scala有scala.collection.mutable.whatever和scala.collection.immutable.whatever,你不能只将可变集合转换为不可变集合,而且你仍然允许你用非const成员改变收集的对象功能

为什么scala具有其他非常好的类型系统,没有任何与C ++ const-keyword相当的东西?

编辑: Margus建议使用import scala.collection.mutable。 我的解决方案是使用

import scala.collection.mutable.HashMap
import scala.collection.immutable.{HashMap => ConstHashMap}
这将使可变HashMap可用作HashMap和不可变的als ConstHashMap,但我仍然更喜欢C ++方法。

4 个答案:

答案 0 :(得分:10)

我不喜欢C ++ const逻辑,它是关于引用而不是引用的对象。如果我有一个“const T *”,则无法保证持有非const指针的人不会修改对象的状态。因此,它无助于避免多线程系统中的竞争条件,也无助于实现持久性容器。

在我看来,拥有一个不可变类的概念并且标准库中缺少不可变容器是任何语言中的错误是非常有帮助的。由于这些应该具有可观察的不变性,但由于效率原因可能需要能够改变内部/不可见状态,我认为const语法几乎没有帮助。

Scala具有我们需要直接使用或基于其他不可变类的不可变类。这非常有价值。额外的语法可能是一个很好的补充,但我可以没有它。

答案 1 :(得分:6)

因为C ++ const在复杂系统中并不是那么好。

  

我可以肯定,调用iPlusFive不会改变对象,也不会意外地在const对象上调用incrementI。

不,你不能,因为实现(可能在图书馆的某个地方看不见)可以抛弃常量。如果没有const,其他语言必须以更安全的方式强制执行不变性(例如,请参阅@ Margus的答案中的Collections.unmodifiableList())。

Const只是编译器读取的文档。文档通常很有帮助,但有时会产生误导。

  

当涉及到集合时,C ++继续使用const集合的const-correct条纹:简单地将你的向量声明为const而你无法改变它。

聚合是const常常让我崩溃的地方。例如,我经常要声明一个方法不会更改向量但可能会更改它的成员(或返回非const成员引用)。我必须使所有const或所有nonconst或为每个组合发明新的类型变体。

'mutable'弥补了一些聚合问题,但引入了更多的复杂性和误用。

答案 2 :(得分:5)

IMO,Scala提供了更大的灵活性,不会将参考的不变性与其背后的结构的可能的可变性混淆,使您能够做出设计决策WRT您正在处理的问题。

答案 3 :(得分:3)

所有Scala代码都被翻译成Java代码,这就是我用Java创建示例的原因。

这是在Java中执行此操作的方法:

    Integer x[] = new Integer[]{1,2,3,4}; 
    final List<Integer> CONST = Collections.unmodifiableList(Arrays.asList(x));

那么你问的是什么? CONST与实际数组相距1步,它可以隐藏实际数组和Collection元素,使用简单数组时,这是不可能的。如果您仍想修改常量集合,则需要复制它并修改它。

来源:链接

  

具有不可修改内容的数组?

     

Java没有提供a的概念   const数组:即一个数组   可以换成新的数组(在C ++中)   术语,“指针可以修改”),   但其元素无法改变。   但是,如果你需要这个   功能,解决方案是   通常很像提供   对任何其他对象的只读访问权限   如下所述。所以几个   可能性是:

     
      
  • 您可以通过传递列表来创建不可修改的列表   Collections.unmodifiableList()(尽管如此   在这种情况下,变量将是   声明类型为List,而不是类型   标志着它“不可修改” - 如   下面讨论,任何修改的尝试   它会在运行时发现;)
  •   
  • 您可以在私有数组周围创建一个包装器对象,并提供   公共方法阅读但不写   它的元素;
  •   
  • 您可以使用只读IntBuffer(或FloatBuffer等):...
  •   

来源:来自c ++的Java equivalents - const

Scala的等价物是:

    val A = Set(1, 2, 3, 4)

    val A = List(1, 2, 3, 4)

这应该转换为:

    scala.collection.immutable.List[java.lang.Integer] A = List(1, 2, 3, 4)

这可能解释了我对IttayD评论的回应,为什么java 6与c ++编译器不同:

  

Reified Generics

     

目前,仿制药已经实施   使用擦除,这意味着   通用类型信息不是   在运行时可用,这使得一些   一种难以编写的代码。泛型   以这种方式实施支持   向后兼容旧版本   非通用代码。具体化的泛型   会制作通用类型   运行时可用的信息,   这将破坏传统的非泛型   码。但是,Neal Gafter有   建议只制作类型   如果指定,以免破裂   向后兼容。

来源:link

愚蠢甚至提到,但Java常量命名约定是使用大写的变量。阅读代码的其他人将立即知道标识符是固定的,不能更改的常量值。

来源:link

  

...你不能只将可变集合转换为不可变集合,......

如果要同时使用集合的可变和不可变版本,那么一个有用的约定是只导入包collection.mutable。

import scala.collection.mutable

来源:Scala Collection API

不确定转换是什么意思,但你可能会这样做:

val a = scala.collection.mutable.List[Int](1, 2, 3)
val A = scala.collection.immutable.List[Int](a.toArray())