D中的const不可变BigInt和range.join [英] const immutable BigInt and range.join in D
问题描述
我正在学习D,而且我一直在使用phobos中定义的越来越多的功能和工具。当参数为常量或不可变时,我遇到了两个无法使用的函数。
I'm learning D and I have been playing with more and more functions and tools defined in phobos. I came across two functions that don't work when the parameters are const or immutable.
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函数也是如此
The same happens with the std.array.join function.
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中完成。
This is quite surprising to me. I have a C++ background so I usually write const everywhere, but it seems I can't do it in D.
作为D用户,我认为一个错误。有人可以向我解释为什么这不是一个错误,以及我应该如何用const /不可变数据调用这些函数?谢谢。
As a D "user", I see that as a bug. Can someone explain me why this is not a bug and how I should call these functions with const/immutable data? Thanks.
推荐答案
首先,我应该说D的 const
是与C ++的 const
完全不同。像C ++一样,理想情况下,您会尽可能地对其进行标记,但是与C ++不同,在D中标记 const
会产生严重的后果。
First off, I should say that D's const
is very different from C++'s const
. Like C++, ideally you'd mark as much with it as possible, but unlike with C++, there are serious consequences to marking something const
in D.
在D中, const
是可传递的,因此它会影响整个类型,而不仅是顶层,而且与C ++不同,您可以t通过抛弃它或使用 mutable
对其进行变异(这是未定义的行为,如果尝试抛弃 const $ c会导致严重的错误。 $ c>,然后对其进行突变)。这两件事的结果是,在很多地方,您无法在D中使用
const
而不会使其无法执行某些操作。
In D, const
is transitive, so it affects the entire type, not just the top level, and unlike in C++, you can't mutate it by casting it away or by using mutable
(it's undefined behavior and will cause serious bugs if you try to cast away const
from an object and then mutate it). The result of those two things is that there are many places where you just can't use const
in D without making it impossible to do certain things.
D的 const
提供真实,可靠的保证,即您不能通过该引用以任何方式,形状或形式来改变对象,而C ++可以 const
只是为了使您不会偶然突变 const
的任何东西,但您可以轻松地将其丢弃 const
并更改对象(具有定义的行为),否则可以通过 const
函数在内部更改对象的对象,谢谢改为可变
。在C ++中,即使没有强制转换或 mutable
const 函数返回对类内部的可变引用,这很简单。 >(例如,从 const
函数返回 vector< int *>
- vector
不能被突变,但是它所指的一切都可以)。由于D保证完全可传递的 const
,并且D提供了这些保证,因此在D中,这些都是不可能的,因此在任何情况下,您需要从<$除非您创建它的全新副本,否则c $ c> const 不会起作用。
D's const
provides real, solid guarantees that you can't mutate the object through that reference in any way, shape or form, whereas C++'s const
just makes it so that you can't mutate anything which is const
by accident, but you can easily cast away const
and mutate the object (with defined behavior), or pieces of the object could be changed internally by const
functions thanks to mutable
. It's also trivial in C++ to return a mutable reference to the internals of a class from a const
function even without casting or mutable
(e.g. returning vector<int*>
from a const
function - the vector
can't be mutated but everything it refers to can be). None of those are possible in D, as D guarantees full transitive const
, and providing those guarantees makes it so that any circumstance where you need to get at something mutable from something const
isn't going to work unless you create an entirely new copy of it.
您应该仔细阅读答案以下问题:
You should probably read over the answers to these questions:
因此,如果您拍打<$ c在D中的所有内容上使用$ c> const ,您会发现有些东西根本行不通。出于与C ++中相同的原因,尽可能多地使用 const
很好,但是成本要高得多,因此您必须对标记内容有更多的限制与 const
。
So, if you're slapping const
on everything in D, you'll find that some things just won't work. Using const
as much as you can is great for the same reasons that it is in C++, but the cost is much higher, so you have to be more restrictive about what you mark with const
.
现在,关于此处的特定问题。 BigInt
被假定为与 const
和不可变
一起使用 code>,但当前不。有很少 打开 错误。我认为很多问题源于 BigInt
使用 COW 在内部,与 const
或不可变$ c $配合使用效果不佳c>。幸运的是,目前在github上有一个 pull请求至少有一些问题,所以我希望
BigInt
将与 const
和 immutable
在不久的将来,但是暂时不能。
Now, as to your specific issue here. BigInt
is supposed to work with const
and immutable
but does not currently. There are a few open bugs on the issue. I believe that a lot of the problem stems from the fact that BigInt
uses COW internally, and that doesn't play nicely with const
or immutable
. Fortunately, there's a pull request on github at the moment which fixes at least some of the problems, so I expect that BigInt
will work with const
and immutable
in the near future, but for the moment, you can't.
关于 join
,您的示例编译正常,因此您将代码复制错误。您的示例中没有 const
。也许你的意思是
As for join
, your example compiles just fine, so you copied your code wrong. There is no const
in your example. Perhaps you meant
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
内部获取的)。
And that doesn't compile. And that's because you aren't passing a valid range of ranges to join
. The type that you'd be passing to join
in that case would be const(int[])[]
. The outer array is mutable, so it's fine, but the inner ones - the ranges that you're trying to join together - are const
, and nothing which is const
can be a valid range, and that's because popFront
won't work. For something to be a valid input range, this code must compile for it (and this is taken from inside of 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
const(int [])
不能与 popFront
配合使用,这是 isInputRange
所要求的。例如,
const(int[])
won't work with popFront
as isInputRange
requires. e.g.
const int[] arr = [1, 2, 3];
arr.popFront();
不会编译,因此 isInputRange
是 false
和 join
不会对其进行编译。
won't compile, so isInputRange
is false
, and join
won't compile with it.
现在,幸运的是,数组有点特殊,因为编译器可以理解它们,因此编译器知道将 const(int [])
转换为<$是完全合法的。 c $ c> const(int)[] 进行切片时。也就是说,它知道给您尾部const切片将无法影响原始数组(因为结果是一个新数组,并且尽管这些元素在数组之间共享,但它们都 const
,因此仍然无法对其进行更改)。因此, arr []
的类型应为 const(int)[]
而不是 const(int [])
,而 [arr1 [],arr2 []]
的类型为 const(int )[] []
,它将与 join
一起使用。因此,您可以
Now, fortunately, arrays are a bit special in that the compiler understands them, so the compiler knows that it's perfectly legit to turn const(int[])
into const(int)[]
when you slice it. That is, it knows that giving you a tail-const slice won't be able to affect the original array (because the result is a new array, and while the elements are shared between the arrays, they're all const
, so they still can't be mutated). So, the type of arr[]
would be const(int)[]
instead of const(int[])
, and the type of [arr1[], arr2[]]
is const(int)[][]
, which will work with join
. So, you can do
const int[] arr1 = [1, 2, 3, 4];
const int[] arr2 = [5, 6];
writeln(join([arr1[], arr2[]]));
,您的代码就可以正常工作。但是,那仅仅是因为您正在使用数组。如果您要处理用户定义的范围,那么当您创建其中一个 const
的那一刻时,您就会陷入困境。这段代码不会编译
and your code will work just fine. However, that's just because you're using arrays. If you were dealing with user-defined ranges, the moment you made one of them const
, you'd be stuck. This code won't compile
const arr1 = filter!"true"([1, 2, 3, 4]);
const arr2 = filter!"true"([5, 6]);
writeln(join([arr1[], arr2[]]));
这是因为编译器不知道它可以安全地从a中获取tail-const切片。 ser定义的类型。它需要知道它可以将 const MyRange!E
转换为 MyRange!(const E)
并具有适当的语义。而且它不知道,因为它们是两个不同的模板实例化,并且它们的内部结构可能完全不同。编写 MyRange
的程序员必须能够编写 opSlice
以便返回 MyRange( const E)
当类型为 const MyRange!E
或 const MyRange!(const E)
,这实际上很难做到(如果没有其他要求,很容易导致递归模板实例化)。巧妙地使用 static if
和 alias this
应该可以,但是很难做到这一点现在就做。关于如何使用户定义的类型合理化,以使 opSlice
返回尾部常量范围,这是一个悬而未决的问题。并且在解决该问题之前,不要混用 const
和范围,因为一旦获得 const
范围,没有办法获得可能包含 popFront
的tail-const切片。因此,一旦您的范围是 const
,它就是 const
。
And that's because the compiler doesn't know that it can safely get a tail-const slice from a ser-defined type. It needs to know that it can convert const MyRange!E
to MyRange!(const E)
and have the proper semantics. And it can't know that, because those are two different template instantiations, and they could have completely different internals. The programmer writing MyRange
would have to be able to write opSlice
such that it returns MyRange(const E)
when the type is const MyRange!E
or const MyRange!(const E)
, and that's actually hard to do (if nothing else, it very easily results in recursive template instantiations). Some clever use of static if
and alias this
should make it possible, but it's hard enough to do that pretty much no one does it right now. It's an open question as to how we're going to make it sane for user-defined types to make opSlice
return a tail-const range. And until that question is solved, const
and ranges just don't mix, because as soon as you get a const
range, there's no way to get a tail-const slice of it which could have popFront
called on it. So, once your range is const
, it's const
.
数组是特殊的,因为它们是内置的,因此只要在适当的时间对其进行切片,就可以避免使用 const
。实际上,模板是使用切片类型而不是原始类型实例化的),但是通常,如果您使用的是范围,请假设不能将其设为 const
。希望这一天有一天会改变,但就目前而言,就是这样。
Arrays are special, since they're built-in, so you can get away with using const
with them as long as you slice them at the appropriate times (and the fact that templates are instantiated with their slice type rather than their original type helps), but in general, if you're using a range, just assume that you can't make it const
. Hopefully, that changes someday, but for now, that's the way that it is.
这篇关于D中的const不可变BigInt和range.join的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!