为什么我要投这个?

时间:2012-04-28 15:19:04

标签: 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());
}

我将该代码放在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?我觉得我在这里做了一些可怕的错误。

3 个答案:

答案 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,那么您必须要getFooconst,否则您必须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,它应该没问题。