逃离“inout hell”

时间:2015-07-20 12:36:57

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

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

2 个答案:

答案 0 :(得分:5)

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

const(Foo) bar(const(Foo) f) {
{
    //...
    return f;
}

并且您将mutableimmutable对象传递给bar,最终返回const个对象,而如果您使用inout

inout(Foo) bar(inout(Foo) f) {
{
    //...
    return f;
}

返回类型与传递给f的参数具有相同的常量。无论哪种方式,在函数内,f都被有效地视为const。您只能在其上调用constinout个功能。因此,使opEquals inout毫无意义,因为它不会返回任何参数。这与制作const相同。

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

  1. Wrapper的{​​{1}}变为可变。然后,当opEquals的{​​{1}}可变时,它可以致电opEquals

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

  3. 如果没有T明确地将opEquals的{​​{1}}转换为opEquals,则无法将T的常量转发给Wrapper。 e.g。

    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; } } } 是一个模板,Wrapperpurenothrow将在其函数中推断,但@safe没有属性推断,constinout

答案 1 :(得分:0)

只需删除inout即可。编译器会自动为模板推送const等属性。

您还没有将inout用于其预期用途; inout用于根据函数的参数将mutable,const或immutable传递给函数的返回类型。函数体必须假设最坏(const),因此它不能调用非常量方法。

请注意,因为在您的示例中,Test.opEquals不是const,所以只能在可变对象上调用它。另请注意,在D中,const是可传递的,因此const(Wrapper!Test)const(Wrapper!(const(Test)))相同。所以无论如何,你都不能在const / immutable对象上调用Test.opEquals(甚至在包装器中),除非你把它const