【问题标题】:How can I return imutable reference types without break constness?如何在不中断常量的情况下返回不可变引用类型?
【发布时间】:2016-05-28 17:51:08
【问题描述】:

我在 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;
}

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

我认为 opIndex() 应该返回 RefType 而不是 const(RefType) 值,因为数组是 Array!RefType

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

【问题讨论】:

  • 尝试将您的const 替换为inout。所以inout(RefType) result() inout { return test[0]; } 看看是否适合你
  • 是的,这行得通!但是 inout 关键字的含义是什么?
  • 我已经测试过并且没有破坏 constness :) 请将其设置为答案。这只是我发现的。
  • inout 可用作 mutable、immutable 和 const 的可变占位符,允许接受其中任何一个。但是 inout 作为参数的行为类似于 scope const 如果我是正确的。

标签: constants d type-safety


【解决方案1】:

我认为 opIndex() 应该返回 RefType 而不是 const(RefType) 值,因为 Array 是 Array!RefType。

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

一个有效的让你可以写成非常量、常量和不可变类型的工作:

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

【讨论】:

  • 是的,这是要走的路。
【解决方案2】:

我认为 opIndex() 应该返回 RefType 而不是 const(RefType) 值,因为 Array 是 Array!RefType。

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

解决此问题的常用方法(例如C++)是定义两个(或在 D 中为三个)重载:

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

因此,对于类是可变的情况,它返回一个可变引用。一种是类为 const 的情况,它返回一个 const 引用,另一种是类是不可变的情况,它返回一个不可变的引用。

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

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

这个单一的定义取代了上述所有三个定义。根据经验,只需像往常一样写下您的定义,但将 inout 放置在您本来应该放置 const 的任何位置。

【讨论】:

  • 好吧,那我昨天有点困惑。我想让我的容器类不能被修改,但我不想阻止用户修改容器持有的对象。那么在这种情况下,没有 const 和 immutable 都是有用的。对吗?
  • @JairoAndresVelascoRomero 是的,因为 constimmutable 在 D 中是可传递的,它们都不适用于您的用例。您必须在 API 级别强制集合本身的不变性。
  • 当您返回 RefType 时,由于传递 const 规则,它也可能需要为 inout - 在 const/immutable/inout 方法中,它们通过 this 到达的所有内容都属于相同类型(或更强)。顺便说一句,很抱歉没有自己写出来,周末很忙。但是有了这个小笨蛋,这个答案应该很好。
  • 好的,最后我的问题应该是“如何从不可变容器返回可变引用?”答案是:你不能来自constimmutable 实例,但你可以在API 设计中强制类的不变性。
猜你喜欢
  • 2021-10-26
  • 2016-08-26
  • 1970-01-01
  • 1970-01-01
  • 2012-09-19
  • 2021-12-05
  • 1970-01-01
  • 2013-09-18
  • 1970-01-01
相关资源
最近更新 更多