【问题标题】:Immutability in D constructorsD 构造函数中的不变性
【发布时间】:2014-06-15 21:11:51
【问题描述】:

我的previous question 讨论过像这样创建一个复制构造函数:

struct Foo {
    int i;

    this(int j) { i = j; }

    this(Foo rhs) { this = rhs; }
}

void main()
{
    auto f = Foo(5);
    auto g = new Foo(f);
}

但是,如果我将i 设为不可变,构造函数将无法编译

错误:无法使用不可变成员修改此 Foo 结构

为什么会这样?我的印象是类或结构的不可变成员在构造函数结束之前不会变为不可变。

【问题讨论】:

    标签: constructor d immutability copy-constructor


    【解决方案1】:

    好的。一般来说,我建议不要使用 immutable 成员的结构。有太多地方可以分配给一个有用的地方。您通常想要对结构执行的操作是使其成为可变的,constimmutable 作为一个整体。在大多数情况下,这很有效。例如

    struct Foo
    {
        int i;
    
        this(int j) { i = j; }
    
        this(Foo rhs) { this = rhs; }
    }
    
    void main()
    {
        immutable f = Foo(5);
    }
    

    编译得很好。通常会导致问题的一个领域是当你必须有一个 postblit 构造函数时,因为它们目前不适用于 constimmutable 结构(这是非常需要修复的东西,但它仍然是一个开放的由于类型系统的工作方式导致的问题 - 它可能导致我们不得不向语言添加复制构造函数,或者我们可能会弄清楚如何去做,但现在它不起作用,而且可能很烦人)。但这仅在您需要 postblit 构造函数时才会影响您,而大多数结构不需要该构造函数(不过,问题最终会得到解决,因为它确实需要;这只是如何以及何时的问题)。

    但是,为了更笼统地回答您的问题,让我们看一个类。例如,这段代码无法编译,因为构造函数不是immutable,并且编译器无法将immutable 类对象转换为可变对象(它可以使用结构来做到这一点,因为它会复制,但是对于一个类,它只是复制引用,而不是对象,所以它不起作用):

    class Foo
    {
        int i;
    
        this(int j) { i = j; }
    }
    
    void main()
    {
        auto f = new immutable Foo(5);
    }
    

    您会得到这个可爱的错误消息,而不是编译:

    q.d(10): Error: mutable method q.Foo.this is not callable using a immutable object
    q.d(10): Error: no constructor for Foo
    

    有三种方法可以解决这个问题。首先是让构造函数immutable

    class Foo
    {
        int i;
    
        this(int j) immutable { i = j; }
    }
    

    这行得通,但它使您只能构造Foos,即immutable,这通常不是您想要的(尽管有时是)。因此,解决问题的第二种方法是将第一种解决方案更进一步并重载构造函数。例如

    class Foo
    {
        int i;
    
        this(int j) { i = j; }
    
        this(int j) immutable { i = j; }
    }
    

    但是,这需要重复代码,这在此处并不多,但对于其他类型可能会很多。所以,一般最好的解决方案是让构造函数pure

    class Foo
    {
        int i;
    
        this(int j) pure { i = j; }
    }
    

    这行得通,因为编译器知道没有任何东西逃脱了构造函数(因为pure保证没有任何东西通过分配给全局或静态变量来逃脱,并且构造函数的参数也不允许任何东西逃脱),并且因为它知道没有对Foo 或其成员的引用可以逃脱构造函数,所以它知道没有其他对这个Foo 的引用,因此它可以安全地将其转换为可变的const 或@987654342 @ 不违反类型系统。当然,只有当你可以使构造函数pure并且没有任何东西可以通过构造函数的参数逃逸时,这才有效,但通常是这种情况,当它不是,你总是可以在可变性上重载构造函数,尽管这是不太可取的。

    如果你真的想要constimmutable 成员,可以在结构上使用相同的技术,但我再次建议不要这样做。这只会给您带来更多麻烦,尤其是在声明变量时只创建整个结构 constimmutable 通常是微不足道的。

    【讨论】:

    • 再次感谢您的宝贵时间!这是一个出色而彻底的答案。对不起,如果这个问题有点奇怪——我在 C++ 上花了很多时间后选择了 D。虽然 D 在很多地方都进行了简化,但我只是想弄清楚哪些习语在 D 中效果很好,哪些不属于。
    【解决方案2】:

    使结构成员immutable 停止分配给结构,这反过来会导致很多问题,您可能需要重新考虑。

    否则直接分配成员而不是依赖赋值运算符:

    struct Foo {
        immutable int i;
    
        this(int j) { i = j; }
    
        this(in Foo rhs) { this.i = rhs.i; }
    }
    

    【讨论】:

    • 你认为不可变成员是一种反模式还是应该避免的?这是我过去在 C++ 中广泛使用的东西,用于使对象不可变,但它确实有缺点。
    • 问题是它会阻止opAssign 工作,这至少可以说是一种痛苦
    • immutable 类中的成员通常没什么大不了的,因为它们是引用类型,但是对于结构,它可能非常烦人,因为你最终会遇到的地方太多了想分配给一个。通常最好有一个没有immutable 成员但可以是immutable 的结构。
    猜你喜欢
    • 2014-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-30
    相关资源
    最近更新 更多