我之前的问题讨论了像这样创建复制构造函数:
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
不可变,则构造函数无法使用
错误:cannot modify struct this Foo with immutable members
为什么会这样?在我的印象中,类或结构的不可变成员直到到达构造函数的末尾才变得不可变。
好的。一般来说,我建议不要使用具有immutable
成员的结构体。在很多地方,赋值给一个值是很有用的。对于结构体,您通常想要做的是使其可以是可变的,const
或immutable
作为一个整体。在大多数情况下,这是可行的。例如
struct Foo
{
int i;
this(int j) { i = j; }
this(Foo rhs) { this = rhs; }
}
void main()
{
immutable f = Foo(5);
}
编译得很好。通常会导致麻烦的一个领域,当你必须有一个postblit构造函数,因为那些目前不使用const
或immutable
结构(这是迫切需要固定的,但它仍然是一个开放的问题由于类型系统是如何工作的——它可能会导致我们不得不拷贝构造函数添加到语言,或者我们可以找出如何做,但现在,它不工作,它可以令人讨厌)。但这只会在你需要postblit构造函数时才会影响到你,而大多数结构都不需要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; }
}
,这是可行的,但它使您只能构造Foo
,即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
。或immutable
,而不违反类型系统。当然,这只有在可以使构造函数为pure
并且没有任何东西可以通过构造函数的参数进行转义的情况下才有效,但这通常是的情况,当不是这样的情况时,您总是可以重载构造函数的可变性,尽管这并不理想。
如果你真的想要const
或immutable
成员,同样的技术也可以用在结构体上,但我还是不建议这样做。它只会给你带来比它值得的更多的麻烦,特别是当它通常是微不足道的,只是使整个结构体const
或immutable
时,声明一个变量。
使immutable
的成员停止对结构体的赋值,这反过来会导致相当多的问题,您可能需要重新考虑。
否则直接赋值,而不依赖于赋值操作符:
struct Foo {
immutable int i;
this(int j) { i = j; }
this(in Foo rhs) { this.i = rhs.i; }
}