我正在学习D而且我一直在玩phobos中定义的越来越多的功能和工具。当参数是const或不可变时,我遇到了两个不起作用的函数。
BigInt i = "42", j = "42";
writeln(i + j);
// This works, except when I add a const/immutable qualifier to i and j
// When const: main.d(23): Error: incompatible types for ((i) + (j)): 'const(BigInt)' and 'const(BigInt)'
// When immutable: main.d(23): Error: incompatible types for ((i) + (j)): 'immutable(BigInt)' and 'immutable(BigInt)'
std.array.join函数也是如此。
int[] arr1 = [1, 2, 3, 4];
int[] arr2 = [5, 6];
writeln(join([arr1, arr2]));
// Again, the const and immutable errors are almost identical
// main.d(28): Error: template std.array.join(RoR, R)(RoR ror, R sep) if (isInputRange!RoR && isInputRange!(ElementType!RoR) && isInputRange!R && is(Unqual!(ElementType!(ElementType!RoR)) == Unqual!(ElementType!R))) cannot deduce template function from argument types !()(const(int[])[])
这对我来说非常令人惊讶。我有一个C ++背景,所以我通常在任何地方写const,但似乎我不能在D中这样做。
作为D“用户”,我将其视为一个错误。有人可以解释为什么这不是一个错误,我应该如何使用const / immutable数据调用这些函数?感谢。
答案 0 :(得分:4)
首先,我应该说D {'{1}}与C ++的const
非常不同。与C ++一样,理想情况下,您尽可能地标记它,但与C ++不同,在D中标记const
会产生严重后果。
在D中,const
是传递性的,因此它影响整个类型,而不仅仅是顶层,而且与C ++不同,你不能通过抛弃它或使用{{1来对其进行变异。 (它是未定义的行为,如果你试图从一个对象中抛弃const
然后变异它会导致严重的错误)。这两件事的结果是,有很多地方你不能在D中使用mutable
而不能做某些事情。
D' const
提供了真实,可靠的保证,您无法以任何方式,形状或形式通过该引用改变对象,而C ++的const
{ 1}}只是为了让你不能偶然地改变const
的任何东西,但你可以轻易地抛弃const
并改变对象(具有已定义的行为)或者由于const
,const
函数可以在内部更改对象。即使没有强制转换或const
(例如从{{1}返回mutable
),在C ++中返回对const
函数的类的内部的可变引用也是微不足道的。函数 - mutable
无法变异,但它引用的所有内容都可以。这些都不可能在D中,因为D保证完全传递vector<int*>
,并且提供这些保证使得任何需要从const
的某些东西获得可变的东西的情况都不会发生除非你创建一个全新的副本。
您应该阅读这些问题的答案:
What is the difference between const and immutable in D?
所以,如果你在D中的所有内容上打vector
,你会发现有些东西只是赢了。尽可能多地使用const
与C ++中的原因相同,但成本要高得多,因此您必须对使用const
标记的内容进行更严格的限制。
现在,关于你的具体问题。 const
假设与const
和const
一起使用,但目前不在。问题上有few open bugs。我认为很多问题源于BigInt
在内部使用COW这一事实,而且与const
或immutable
并没有很好的协作。幸运的是,目前还有pull request on github至少解决了一些问题,所以我希望BigInt
可以与const
和immutable
一起使用未来,但目前,你不能。
对于BigInt
,您的示例编译得很好,因此您错误地复制了代码。您的示例中没有const
。也许你的意思是
immutable
没有编译。这是因为您没有将有效范围传递给join
。在这种情况下,您传递给const
的类型为const int[] arr1 = [1, 2, 3, 4];
const int[] arr2 = [5, 6];
writeln(join([arr1, arr2]));
。外部数组是可变的,所以它很好,但内部数组 - 你尝试连接在一起的范围 - 是join
,没有任何join
可以是一个有效的范围,因为const(int[])[]
不起作用。对于某些有效的输入范围,此代码必须为其编译(这取自const
)。
const
popFront
无法按std.range.isInputRange
要求使用 R r = void; // can define a range object
if (r.empty) {} // can test for empty
r.popFront(); // can invoke popFront()
auto h = r.front; // can get the front of the range
。 e.g。
const(int[])
不会编译,因此popFront
为isInputRange
,const int[] arr = [1, 2, 3];
arr.popFront();
无法使用它进行编译。
现在,幸运的是,数组有点特别,因为编译器可以理解它们,因此编译器知道在切片时将isInputRange
转换为false
是完全合法的。也就是说,它知道给你一个tail-const切片不会影响原始数组(因为结果是一个新数组,虽然这些元素在数组之间共享,但它们都是全部join
,所以他们仍然无法发生变异。因此,const(int[])
的类型将是const(int)[]
而不是const
,而arr[]
的类型是const(int)[]
,它将与const(int[])
一起使用。所以,你可以做到
[arr1[], arr2[]]
你的代码就可以了。但是,这仅仅是因为您正在使用数组。如果您正在处理用户定义的范围,那么当您制作其中一个const(int)[][]
时,您就会被卡住。这段代码不会编译
join
这是因为编译器不知道它可以安全地从ser定义的类型获得尾部const片。它需要知道它可以将const int[] arr1 = [1, 2, 3, 4];
const int[] arr2 = [5, 6];
writeln(join([arr1[], arr2[]]));
转换为const
并具有适当的语义。并且它无法知道,因为它们是两个不同的模板实例,并且它们可能具有完全不同的内部结构。编写const arr1 = filter!"true"([1, 2, 3, 4]);
const arr2 = filter!"true"([5, 6]);
writeln(join([arr1[], arr2[]]));
的程序员必须能够编写const MyRange!E
,以便在类型为MyRange!(const E)
或MyRange
时返回opSlice
,并且&#39}实际上很难做到(如果没有别的,它很容易导致递归模板实例化)。巧妙地使用MyRange(const E)
和const MyRange!E
应该可以实现,但是很难做到这一点,现在几乎没有人这样做。这是一个悬而未决的问题,关于我们如何使用户定义的类型能够让const MyRange!(const E)
返回尾部常量范围。在问题解决之前,static if
并且范围不会混合,因为一旦得到alias this
范围,就无法获得尾部常量切片它可以opSlice
调用它。因此,一旦您的范围为const
,就会const
。
数组是特殊的,因为它们是内置的,因此只要您在适当的时间对它们进行切片(并且模板实例化的事实),您就可以使用popFront
。他们的切片类型而不是原始类型有帮助),但一般情况下,如果您使用范围,只需假设您无法使其成为const
。希望有一天会发生变化,但就目前来说,情况就是如此。