D中的const不可变BigInt和range.join [英] const immutable BigInt and range.join in D

查看:50
本文介绍了D中的const不可变BigInt和range.join的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习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 ,然后对其进行突变)。这两件事的结果是,在很多地方,您无法在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:

逻辑常量D

D中的const和不可变之间有什么区别?

因此,如果您拍打<$ 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 不可变。幸运的是,目前在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆