D 逃离;“我没有地狱”;

D 逃离;“我没有地狱”;,d,D,我有一些非常简单的代码拒绝编译: struct Wrapper(T) { T t; bool opEquals(inout(Wrapper) other) inout { return t == other.t; } bool opEquals(inout(T) val) inout { return t == val; } } struct Test { bool opEquals(Test

我有一些非常简单的代码拒绝编译:

struct Wrapper(T)
{
    T t;

    bool opEquals(inout(Wrapper) other) inout
    {
        return t == other.t;
    }

    bool opEquals(inout(T) val) inout
    {
        return t == val;
    }
}

struct Test
{
    bool opEquals(Test t)
    {
        return true;
    }
}

void main()
{
    Wrapper!Test a, b;

    assert(a == b);

    //Error: inout method Test.opEquals is not 
    //callable using a mutable object
    assert(a == Test());
}

现在,我知道了问题所在,那就是
Test
没有定义
inout
opEquals
。但是,在
Test
中定义另一个可变版本的
opEquals
并不能解决此问题,因为编译器只是忽略它并调用
inout
版本。我有没有办法解决这个问题,而不用为可变、
const
immutable
定义一个
opEquals
重载?

只需删除
inout
。编译器会自动为模板推断属性,如
const

无论如何,您并没有将
inout
用于其预期目的;用于根据函数的参数将mutable、const或immutable传输到函数的返回类型。函数体必须假设最坏(const),因此不能调用非常量方法


请注意,在您的示例中,
Test.opEquals
不是
const
,因此只能对可变对象调用它。还要注意,在D中,
const
是可传递的,因此
const(Wrapper!Test)
const(Wrapper!(const(Test)))是相同的。因此,不管怎样,都不能对常量/不可变对象(即使在包装器中)调用
Test.opEquals
,除非将其设置为
const

所有
inout
都是为了使其返回类型的常量与函数参数的常量匹配。如果你有

const(Foo) bar(const(Foo) f) {
{
    //...
    return f;
}
mutable
immutable
对象传递给
bar
,最终返回
const
对象,而如果使用
inout

inout(Foo) bar(inout(Foo) f) {
{
    //...
    return f;
}
返回类型的常量与传递给
f
的参数的常量相同。无论哪种方式,在函数中,
f
都被有效地视为
const
。只能对其调用
const
inout
函数。因此,使
opEquals
inout
无意义,因为它不返回任何参数。这与将其设置为
const
相同

这里的基本问题是,您试图调用
常量
对象上的可变函数。这是不合法的,因为它违反了
const
。您有两种选择之一:

  • 使
    Wrapper
    opEquals
    可变。然后,当
    T
    opEquals
    是可变的时,它可以调用
    opEquals

  • 根据
    T
    定义
    opEquals
    的方式,使用
    static if
    以不同方式定义
    opEquals

  • 如果不使用
    static if
    显式执行,则无法将
    opEquals
    的常量从
    T
    转发到
    Wrapper
    。e、 g

    struct Wrapper(T)
    {
        T t;
    
        static if(is(typeof({const T t; t == t;})))
        {
            bool opEquals(const Wrapper other) const
            {
                return t == other.t;
            }
    
            bool opEquals(const T val) const
            {
                return t == val;
            }
        }
        else
        {
            bool opEquals(Wrapper other)
            {
                return t == other.t;
            }
    
            bool opEquals(T val)
            {
                return t == val;
            }
        }
    }
    

    由于
    Wrapper
    是一个模板,
    pure
    nothrow
    @safe
    将在其函数上进行推断,但是
    const
    inout
    不可变的
    Wrapper
    中的第二个
    opEquals
    方法拼写错误。另外,因为
    opEquals
    不修改它的参数,您可以将它设置为
    const
    ,并且它可以在可变和不可变类型上工作。我认为问题在于我将
    Wrapper.opEquals
    标记为
    inout
    ,而不是将参数标记为
    inout
    。从方法中删除
    inout
    会导致它编译,但是如果我创建了一个
    不可变的
    常量
    包装器
    ,它将不起作用。如果您创建
    opEquals
    常量,它将起作用,您无论如何都应该这样做,因为
    opEquals
    不应该修改任何内容。您的示例缺少了一个关键部分:一个用户定义的类型,带有精心设计的
    opEquals
    。查看我的修改:您可能需要使用
    static if
    来查看包装类型的
    opEquals
    是否为const,并在此基础上定义一个可变或常量
    opEquals
    (inout更多的是将const/immutable转换为返回类型,布尔型不需要)。或使测试的
    opEquals
    const,或使用
    别名t this
    我无法从
    opEquals
    中删除
    inout
    ,因为这样它将无法处理
    不可变的
    常量
    对象。让我们往下看
    const
    ,这样我可以更好地说明我的问题。看看,你明白为什么它不能编译,为什么我不能从
    包装器中删除
    inout
    ?@Meta它不能编译,因为
    Test.opEquals
    不是
    const
    。没有包装也不行<代码>断言(const(Test)(==const(Test)()
    是的,所以我们回到我原来的问题。我想这意味着答案是“不”;您试图对常量对象调用
    Test.opEquals
    ,但
    Test.opEquals
    不是常量。它与您的包装器或
    inout
    无关。我的困惑源于错误的假设,即
    bool opEquals inout
    应该在
    wrapper
    是可变的情况下充当可变的。我想我现在已经很好地理解了为什么这样做行不通。。。如果你想让你的类型正确地处理任何“constness”,这真的是一个很大的难题。@Meta是的,一种向前处理constness的方法是有用的,但它到底应该如何工作成了一个有趣的问题。以前已经讨论过了,但还没有讨论过。幸运的是,它只在包装类型时才会出现,这主要发生在范围上,范围和“const”的交互非常糟糕,以至于像
    front
    empty
    这样的函数缺少
    const
    实际上并不重要。