如何在不破坏常量的情况下 return 不可变引用类型?

How can I return imutable reference types without break constness?

我在D:

中有如下代码
import std.stdio;
import std.container.array;

class RefType { }

class MyContainer {
    private Array!RefType test;
    RefType result() const {  // I want const on this, not on return type
        return test[0];       // use opIndex() from Array(T)
        // Error: cannot implicitly convert expression (this.test.opIndex(0u)) 
        // of type const(RefType) to main.RefType
    }
}

int main(string[] argv) {
    auto c = new MyContainer; auto r = c.result(); return 0;
}

如您所见,我想 return 来自自定义容器 class 的引用类型。但是 Array 的 opIndex() 没有给出这样做的权限,为什么?

我认为 opIndex() 应该 return 一个 RefType 而不是 const(RefType) 值,因为数组是 Array!RefType.

这是一个错误吗?还是设计意图?如果这是有意的设计,我怎样才能得到我想要的?

I think that opIndex() should return a RefType instead of a const(RefType) value because the Array is Array!RefType.

不,这是错误的。当您将 memthod 定义为 const 时,您还指定了 class 的所有成员也是常量。这意味着,无论成员在定义中的类型是什么,在您的 result 方法中它们(至少)是 const。因此,编译器完全有理由返回它正在返回的错误。

通常(例如 C++)解决这个问题的方法是定义两个(或者,在 D 中,三个)重载:

RefType result() {
    return test[0];
}
RefType result() const {
    return test[0];
}
RefType result() immutable {
    return test[0];
}

所以,对于 class 是可变的情况,returns 是一个可变引用。一种情况是 class 是 const,returns 是一个 const 引用,另一种情况是 class 是不可变的,returns 是一个不可变引用。

然而,您会注意到,除了方法装饰之外,这三个实现完全相同。为了防止您定义三个相同的实现,D 具有 inout 修饰符:

RefType result() inout {
    return test[0];
}

这个单一的定义取代了上面的所有三个定义。根据经验,只需像往常一样编写您的定义,但将 inout 放置在您原本要放置 const.

的任何位置

I think that opIndex() should return a RefType instead of a const(RefType) value because the Array is Array!RefType.

这个假设是错误的。由于您的 opIndex 方法被标记为常量。隐式给出的 this reference 也是 const (is(this == const(MyContainer))。这意味着您通过它访问的所有内容也是 const,因为 D 的 const 是可传递的。当您在 const 方法中访问某些 const 时,您将得到一些 const。返回一些非 const来自此方法的方法在 D 中是非法的(幸运的是)。唯一可以从 const 方法返回的非 const 是没有间接寻址的值类型。

使您可以将类型作为非常量、常量和不可变类型工作的有效方法,您可以这样写:

inout(RefType) result() inout { ... }