逃离"inout hell"

Met*_*eta 3 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 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());
}
Run Code Online (Sandbox Code Playgroud)

现在,我知道问题,这是Test没有定义的问题inout opEquals.但是,定义另一个可变版本的opEqualsin Test不能解决这个问题,因为编译器只是忽略它并且inout无论如何调用版本.有没有办法让我解决这个问题,而不需要opEquals为mutable 定义一个重载const,和immutable

Jon*_*vis 5

所有这些inout都是为了使返回类型的常量可以匹配函数参数的常量.如果你有

const(Foo) bar(const(Foo) f) {
{
    //...
    return f;
}
Run Code Online (Sandbox Code Playgroud)

并且你传递一个mutable或一个immutable对象bar,你得到一个const返回的对象,而如果你使用inout

inout(Foo) bar(inout(Foo) f) {
{
    //...
    return f;
}
Run Code Online (Sandbox Code Playgroud)

返回类型与传递给的参数具有相同的常量f.无论哪种方式,在函数内,都f被有效地视为const.您只能在其上呼叫constinout运行.因此,制作opEquals inout毫无意义,因为它不会返回任何参数.它与制作它是一样的const.

这里你的根本问题是你试图在一个const对象上调用一个可变函数.这不合法,因为它违反了const.您有以下两种选择之一:

  1. 制作WrapperopEquals可变.然后,它可以调用opEqualsTopEquals是可变的.

  2. 用于static if根据定义的方式进行opEquals不同的T定义opEquals.

有没有办法来转发的常量性opEquals来自TWrapper不具有明确地这样做static if.例如

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

由于Wrapper是一个模板,pure,nothrow,和@safe将在其职能推断,但没有属性推断const,inoutimmutable.