简体   繁体   English

D构造函数的不变性

[英]Immutability in D constructors

My previous question discussed making a copy constructor like so: 之前的问题讨论过如下制作复制构造函数:

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);
}

But, if I make i immutable, the constructor fails to compile with 但是,如果我使i不可变,构造函数无法编译

Error: cannot modify struct this Foo with immutable members 错误:无法使用不可变成员修改struct this Foo

Why is this the case? 为什么会这样? I was under the impression that immutable members of a class or struct do not become immutable until the end of the constructor is reached. 我的印象是,在到达构造函数的末尾之前,类或结构的不可变成员不会变为不可变。

Okay. 好的。 In general, I'd advise against having structs with immutable members. 一般来说,我建议不要使用immutable成员。 There are just too many places where it's useful to be able to assign to one. 有太多的地方可以分配给一个有用的地方。 What you typically want to do with a struct is make it so that it can be mutable, const , or immutable as a whole. 你通常想要对结构做什么是使它成为一个整体可变, constimmutable的。 And for the most part, that just works. 而在大多数情况下,这才有效。 eg 例如

struct Foo
{
    int i;

    this(int j) { i = j; }

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

void main()
{
    immutable f = Foo(5);
}

compiles just fine. 编译得很好。 The one area that generally causes trouble with that is when you have to have a postblit constructor, because those don't currently work with const or immutable structs (it's something that sorely needs to be fixed, but it's still an open problem due to how the type system works - it may result in us having to add copy constructors to the language, or we may figure out how to do it, but for now, it doesn't work, and it can be annoying). 通常会导致问题的一个方面是当你必须有一个postblit构造函数时,因为那些当前不使用constimmutable结构(这是一个非常需要修复的东西,但它仍然是一个开放的问题,因为如何类型系统工作 - 它可能导致我们不得不在语言中添加复制构造函数,或者我们可能会弄清楚如何执行它,但是现在,它不起作用,并且它可能很烦人)。 But that only affects you if you need a postblit constructor, which most structs don't need (the problem will be fixed eventually though, because it really needs to be; it's just a question of how and when). 但是这只会影响你,如果你需要一个postblit构造函数,大多数结构不需要(问题最终会被修复,因为它确实需要;它只是一个如何和何时的问题)。

However, to answer your question more generally, let's look at a class. 但是,为了更广泛地回答你的问题,让我们来看一个班级。 For instance, this code won't compile, because the constructor is not immutable , and the compiler can't convert an immutable class object to a mutable one (it can do that with structs, because it makes a copy, but with a class, it's just copying the reference, not the object, so it doesn't work): 例如,这段代码不会编译,因为构造函数不是immutable ,并且编译器不能将immutable类对象转换为可变类(它可以使用结构来实现,因为它创建了一个副本,但是有一个类,它只是复制引用,而不是对象,所以它不起作用):

class Foo
{
    int i;

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

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

Instead of that compiling, you get this lovely error message: 而不是编译,你得到这个可爱的错误信息:

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

There are three ways to solve this. 有三种方法可以解决这个问题。 The first is to make the constructor immutable 第一个是使构造函数immutable

class Foo
{
    int i;

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

and that works, but it makes it so that you can only construct Foo s which are immutable , which usually isn't what you want (though it sometimes is). 这是有效的,但它使得你只能构造immutable Foo ,这通常不是你想要的(虽然它有时是)。 So, the second way to solve the problem would be to take the first solution a step further and overload the constructor. 因此,解决问题的第二种方法是将第一个解决方案更进一步,并使构造函数重载。 eg 例如

class Foo
{
    int i;

    this(int j) { i = j; }

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

However, that requires code duplication, which isn't a lot here, but it could be a lot for other types. 但是,这需要代码重复,这在这里并不多,但对于其他类型可能会有很多。 So, what is generally the best solution is to make the constructor pure . 因此,通常最好的解决方案是使构造函数pure

class Foo
{
    int i;

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

This works, because the compiler then knows that nothing has escaped the constructor (since pure guarantees that nothing escapes by being assigned to a global or static variable, and the constructor's parameters don't allow anything to escape either), and because it knows that no references to Foo or its members can escape the constructor, it knows that there are no other references to this Foo and that it therefore can safely convert it to mutable, const , or immutable without violating the type system. 这是有效的,因为编译器然后知道没有任何东西已经转义构造函数(因为pure保证通过分配给全局或静态变量没有任何转义,并且构造函数的参数也不允许任何东西转义),并且因为它知道没有对Foo或其成员的引用可以转义构造函数,它知道没有其他对此Foo引用,因此它可以安全地将其转换为mutable, constimmutable而不违反类型系统。 Of course, that only works if you can make the constructor pure and nothing can escape via the constructor's arguments, but that's usually the case, and when it isn't, you can always just overload the constructor on mutability, much as that's less desirable. 当然,这只有在你可以使构造函数pure并且没有任何东西可以通过构造函数的参数进行转义时才有效,但通常就是这种情况,当它不存在时,你总是可以在可变性上重载构造函数,就像那样不太可取。 。

The same techniques can be used on structs if you really want const or immutable members, but again, I'd advise against it. 如果你真的想要constimmutable成员,可以在结构上使用相同的技术,但同样,我建议反对它。 It's just going to cause you more trouble than it's worth, especially when it's usually trivial to just make the whole struct const or immutable when declaring a variable. 这只会给你带来更多的麻烦而不是它的价值,特别是在声明一个变量时,只需要使整个struct constimmutable一般都是微不足道的。

Making members of structs immutable stops assigning to the struct which in turn causes quite a few issues, you may which to rethink that. 使结构成员immutable停止分配给结构,这反过来会导致相当多的问题,你可能会重新考虑这一点。

Otherwise assign the members directly instead of relying on the assignment operator: 否则直接分配成员而不是依赖赋值运算符:

struct Foo {
    immutable int i;

    this(int j) { i = j; }

    this(in Foo rhs) { this.i = rhs.i; }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM