我有一些非常简单的代码拒绝编译:
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
版本。我是否有办法解决此问题,而无需为可变,opEquals
和const
定义immutable
重载?
答案 0 :(得分:5)
所有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
的{{1}}变为可变。然后,当opEquals
的{{1}}可变时,它可以致电opEquals
。
根据T
定义opEquals
的方式,使用static if
以不同的方式定义opEquals
。
如果没有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;
}
}
}
是一个模板,Wrapper
,pure
和nothrow
将在其函数中推断,但@safe
没有属性推断,const
或inout
。
答案 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
。