2015-07-20 48 views
3

我逃离有一些非常简单的代码,拒绝编译:从“INOUT地狱”

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没有定义的inoutopEquals。但是,在Test中定义opEquals的另一个可变版本不能解决此问题,因为编译器只是忽略它并无论如何调用inout版本。有没有什么方法可以解决这个问题,而不是诉诸于为易变,constimmutable定义opEquals过载?

+3

你的第二个'Wrapper'中的'opEquals'方法拼写错误。另外,因为'opEquals'不修改它的参数,所以你可以把它设为'const',它可以在可变类型和不可变类型上工作。 –

+0

我认为问题在于,我将'Wrapper.opEquals'标记为'inout',而不是将参数标记为'inout'。从方法中删除'inout'会导致它编译,但如果我创建了'immutable'或'const''Wrapper',那么它将不起作用。 – Meta

+0

如果你使'opEquals'为const,你应该这样做,因为opEquals不应该修改任何东西。 http://dpaste.dzfl.pl/6cab4a419488 –

回答

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功能。所以,制作opEqualsinout是毫无意义的,因为它没有返回任何参数。这与制作const相同。

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

  1. 制作WrapperopEquals可变的。那么当TopEquals是可变的时,它可以调用opEquals

  2. 使用static if来定义opEquals的不同取决于T如何定义opEquals

没有办法的opEqualsT常量性转发给Wrapper没有与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; 
     } 
    } 
} 

因为Wrapper是一个模板,purenothrow@safe将其功能可以推断,但对于constinout,或immutable没有属性的推理。

+0

我的困惑源于错误的假设,即当'Wrapper'是可变的时,'bool opEquals inout'应该可以变为可变的。我想我现在已经有了一个很好的理解,确切地说明为什么这种方法不起作用......如果你想让你的类型在任何“常量”下正常工作,这真是一大痛苦。 – Meta

+0

@Meta是的,一种转发常量的方法将会很有用,但究竟应该如何工作就成了一个有趣的问题。之前已经讨论过但尚未到任何地方。幸运的是,它只是在包装类型时才会出现,而这种情况主要发生在范围内,范围和'const'交互严重,以至于像'front'和'empty'这样的函数缺少'const'实际上并不重要。 –

1

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

无论如何,您并未使用inout作为其预定用途; inout用于根据参数传递mutable,const或immutable到函数的返回类型。函数体必须承担最差(const),所以它不能调用非常量方法。

请注意,因为在您的示例中,Test.opEquals不是const,它只能在可变对象上调用。还要注意,在D中,const是可传递的,因此const(Wrapper!Test)const(Wrapper!(const(Test)))相同。因此,无论如何,除非您创建const,否则无法在const /不可变对象上(即使在包装器中)调用Test.opEquals

+0

我无法从'opEquals'中删除'inout',因为它不会与'immutable'或'const'对象一起使用。让我们下降到'const',以便更好地说明我的问题。看看[下面的代码](http://dpaste.dzfl.pl/a3242543f31e),你明白它为什么不编译,为什么我不能从'Wrapper.opEquals'中移除'inout'? – Meta

+0

@Meta它不能编译,因为'Test.opEquals'不是'const'。没有包装也不行。 'assert(const(Test)()== const(Test)())' –

+0

是的,所以我们回到了我原来的问题。我想这意味着答案是“否”。 – Meta