我已将此问题浓缩为一个小代表性样本:
import std.stdio;
class Foo
{
private int f;
}
class State
{
private Foo foo;
const Foo getFoo()
{
return foo; // This line here.
}
}
void main()
{
auto s = new State;
writeln(s.getFoo());
}
我将该代码放在test.d
。
$ gdmd test.d
test.d:13: Error: cannot implicitly convert expression (this.foo) of type const(Foo) to test.Foo
我知道它告诉我用cast(test.Foo)foo
转换返回值,但为什么呢?为什么它将成员解释为const(Foo)
类型,为什么需要我抛弃const
?我觉得我在这里做了一些可怕的错误。
答案 0 :(得分:5)
const Foo getFoo()
与
相同Foo getFoo() const
它会生成不可见的this
参数const
。因为const
在D中是可传递的,这意味着当您尝试从this
返回成员变量时,它也将是const
。如果foo
是值类型,那么它只会复制它,然后返回一个可变Foo
不会有问题,因为它不会影响原始值。但是Foo
是一个类,因此是一个引用类型。因此,返回foo
将返回对State
所持有的完全相同对象的引用。没有复制。因此必须为const
- 否则您将违反this
参数的常量。
不,抛弃const
不是一个好的解决方案。正如this question中所讨论的那样,丢弃const
然后在D中改变该值实际上是非法的。当你这样做时,你违反了类型系统。编译器会让你这样做,但是当你这样做时,你会把自己的生命掌握在自己手中。如果底层对象实际上是immutable
则特别糟糕,因为在这种情况下您可以获得段错误。你绝对不得不抛弃const
,而你从不改变它,除非你真的知道你在做什么。
不,正确的解决方案是返回类型const
:
const(Foo) getFoo() const
现在,您可以返回foo
并使用它。如果您想要一个可变的Foo
,那么您必须要getFoo
为const
,否则您必须getFoo
返回foo
的副本。 e.g。
Foo getFoo() const
{
//This would be cleaner if you provided a clone/dup function
//of some kind on Foo.
auto retval = new Foo;
retval.f = foo.f;
return retval;
}
从这里拿走的重要一点是,D中的const
是传递。正如沃尔特·布莱特(Walter Bright)所说的那样,“它一直都是乌龟。”一旦有const
个内容,所有部分都是const
,并且不会抛弃const
来绕过它,除非你真的知道你在做什么。因此,如果要返回引用const
函数中的成员变量的引用类型,则需要创建返回类型const
或创建它的副本并返回它。
答案 1 :(得分:4)
我怀疑你真正想要的是这个:
class State
{
private Foo foo;
// When `this` is const, return const(Foo).
// When `this` is immutable, return immutable(Foo).
// When `this` is mutable, return Foo.
inout(Foo) getFoo() inout
{
return foo;
}
}
相当于:
class State
{
private Foo foo;
Foo getFoo()
{
return foo;
}
const(Foo) getFoo() const
{
// `this.foo` is of type const(Foo), as const is transitive.
return foo;
}
immutable(Foo) getFoo() immutable
{
// `this.foo` is of type immutable(Foo), as immutable is transitive.
return foo;
}
}
答案 2 :(得分:3)
它将该成员解释为const(Foo)
,因为您的方法签名为const Foo getFoo()
,这意味着foo
不会是可变的。
删除const
,它应该没问题。