D构造函数的不变性

Mat*_*ine 4 constructor d immutability copy-constructor

之前的问题讨论过如下制作复制构造函数:

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);
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我使用i不可变,则构造函数无法编译

错误:无法使用不可变成员修改struct this Foo

为什么会这样?我的印象是,在到达构造函数的末尾之前,类或结构的不可变成员不会变为不可变.

Jon*_*vis 9

好的.一般来说,我建议不要与immutable会员结成.有太多的地方可以分配给一个有用的地方.你通常想要对结构做什么是使它可以变得可变const,或immutable作为一个整体.而在大多数情况下,这才有效.例如

struct Foo
{
    int i;

    this(int j) { i = j; }

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

void main()
{
    immutable f = Foo(5);
}
Run Code Online (Sandbox Code Playgroud)

编译得很好.通常会导致问题的一个方面是当你必须有一个postblit构造函数时,因为那些当前不能使用constimmutable结构化(这是一个非常需要修复的东西,但由于类型的原因,它仍然是一个开放的问题系统工作 - 它可能导致我们不得不在语言中添加复制构造函数,或者我们可能会弄清楚如何执行它,但是现在,它不起作用,并且它可能很烦人).但是这只会影响你,如果你需要一个postblit构造函数,大多数结构不需要(问题最终会被修复,因为它确实需要;它只是一个如何和何时的问题).

但是,为了更广泛地回答你的问题,我们来看一个班级.例如,这段代码不会编译,因为构造函数不是immutable,编译器不能将immutable类对象转换为可变类(它可以用结构体来做,因为它可以复制,但是有了类,它是只是复制引用,而不是对象,所以它不起作用):

class Foo
{
    int i;

    this(int j) { i = j; }
}

void main()
{
    auto f = new immutable Foo(5);
}
Run Code Online (Sandbox Code Playgroud)

而不是编译,你得到这个可爱的错误信息:

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

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

class Foo
{
    int i;

    this(int j) immutable { i = j; }
}
Run Code Online (Sandbox Code Playgroud)

这是有效的,但它使得你只能构造Foos immutable,这通常不是你想要的(虽然它有时是).因此,解决问题的第二种方法是将第一个解决方案更进一步,并使构造函数重载.例如

class Foo
{
    int i;

    this(int j) { i = j; }

    this(int j) immutable { i = j; }
}
Run Code Online (Sandbox Code Playgroud)

但是,这需要代码重复,这在这里并不多,但对于其他类型可能会有很多.因此,通常最好的解决方案是制作构造函数pure.

class Foo
{
    int i;

    this(int j) pure { i = j; }
}
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为编译器然后知道没有任何东西已经转义构造函数(因为pure保证通过分配给全局变量或静态变量没有任何转义,并且构造函数的参数也不允许任何东西转义),并且因为它知道没有引用Foo或其成员可以转义构造函数,它知道没有其他引用Foo,因此它可以安全地将其转换为可变const,或immutable不违反类型系统.当然,这只有在你可以创建构造函数pure并且没有任何东西可以通过构造函数的参数进行转义时才有效,但通常就是这种情况,当它不是时,你总是可以在构造函数上重载可变性,就像那些不太理想一样.

如果你真的想要constimmutable成员,可以在结构上使用相同的技术,但同样,我建议反对它.它只会给你带来更多麻烦而不是它的价值,特别是当它只是制作整个结构constimmutable声明一个变量时通常是微不足道的.