我有一些非常简单的代码拒绝编译:
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
版本。有没有办法让我解决这个问题而不诉诸为mutable,opEquals
和const
定义一个immutable
重载?
所有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
对象上调用一个可变函数。这不合法,因为它违反了const
。您有以下两种选择之一:
Wrapper
的opEquals
可变。然后它可以在opEquals
的T
可变时调用opEquals
。static if
如何定义opEquals
,使用T
来定义opEquals
。没有办法将opEquals
的T
的常量转发到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
是一个模板,pure
,nothrow
和@safe
将根据其函数推断,但const
,inout
或immutable
没有属性推断。
只需删除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
。