为什么我要投这个?

nmi*_*els 4 d

我把这个问题浓缩成了一个小代表性样本:

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

我把那些代码放进去了test.d.

$ gdmd test.d
test.d:13: Error: cannot implicitly convert expression (this.foo) of type const(Foo) to test.Foo
Run Code Online (Sandbox Code Playgroud)

我知道它告诉我要使用返回值cast(test.Foo)foo,但为什么呢?为什么它会将成员解释为类型const(Foo),为什么需要我抛弃它const?我觉得我在这里做了一些可怕的错事.

Jon*_*vis 5

const Foo getFoo()
Run Code Online (Sandbox Code Playgroud)

是相同的

Foo getFoo() const
Run Code Online (Sandbox Code Playgroud)

它使隐形this参数const.因为const在D中是传递的,这意味着当你尝试从中返回一个成员变量时this,它也会const是这样.如果foo是值类型,那么它只是复制它,然后返回一个mutable Foo不会有问题,因为它不会影响原始.但是Foo是一个类,因此是一个引用类型.因此,返回foo返回对完全相同的对象的引用State.没有复制.所以它必须const-否则你会被违反的常量性this参数.

不,虚掷const不是一个很好的解决方案.正如在这个问题中讨论的那样,const在D中抛弃然后改变该值实际上是非法的.当你这样做时,你违反了类型系统.编译器会让你这样做,但是当你这样做时,你会把自己的生命掌握在自己手中.如果底层对象实际上是特别糟糕的immutable,因为在这种情况下您可以获得段错误.const如果你绝对必须,你只会抛弃它,除非你真的知道你在做什么,否则你永远不会改变它.

不,正确的解决方案是制作返回类型const:

const(Foo) getFoo() const
Run Code Online (Sandbox Code Playgroud)

现在,您可以返回foo并使用它.如果你想要一个可变的Foo,那么你要么必须没有getFoo成为const,或者你必须有getFoo回报的副本foo.例如

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

你从这里带走的重要一点是constD在传递中.正如沃尔特·布莱特(Walter Bright)所说的那样,"它一直都是乌龟." 一旦有什么东西const,它的所有部分都是const,并且不会被抛弃去除const它,除非你真的知道你在做什么.因此,如果要返回引用const函数的成员变量的引用类型,则需要const创建返回类型或创建它的副本并返回该类型.