逃离 "inout hell"
Escaping from "inout hell"
我有一些非常简单的代码拒绝编译:
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
。然而,在 Test
中定义 opEquals
的另一个可变版本并不能解决这个问题,因为编译器只是忽略它并调用 inout
版本。有什么方法可以解决这个问题,而无需为可变的 const
和 immutable
定义 opEquals
重载?
只需删除 inout
。编译器会自动为模板推断 const
等属性。
无论如何,您并未将 inout
用于其预期目的; inout
用于根据参数将可变、常量或不可变转换为函数的 return 类型。函数体必须假设最坏的情况(const),所以它不能调用非常量方法。
请注意,由于在您的示例中 Test.opEquals
不是 const
,因此它只能在可变对象上调用。另请注意,在 D 中,const
是可传递的,因此 const(Wrapper!Test)
与 const(Wrapper!(const(Test)))
相同。所以无论如何,你不能在 const/immutable 对象上调用 Test.opEquals
(即使在包装器中),除非你使它成为 const
.
所有 inout
都是为了使 return 类型的常量可以匹配函数参数的常量。如果你有
const(Foo) bar(const(Foo) f) {
{
//...
return f;
}
然后你将一个 mutable
或 immutable
对象传递给 bar
,你最终得到一个 const
对象 returned,而如果你使用inout
inout(Foo) bar(inout(Foo) f) {
{
//...
return f;
}
return 类型与传递给 f
的参数具有相同的常量性。无论哪种方式,在函数内,f
都被有效地视为 const
。您只能在其上调用 const
和 inout
函数。因此,使 opEquals
inout
毫无意义,因为它没有 return 任何参数。和制作const
.
一样
这里的根本问题是您试图在 const
对象上调用可变函数。这是不合法的,因为它违反了 const
。您有两个选项之一:
使 Wrapper
的 opEquals
可变。然后它可以在 T
的 opEquals
可变时调用 opEquals
。
根据T
如何定义opEquals
,使用static if
定义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
会在其函数上进行推断,而[=17则没有属性推断=]、inout
或 immutable
.
我有一些非常简单的代码拒绝编译:
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
。然而,在 Test
中定义 opEquals
的另一个可变版本并不能解决这个问题,因为编译器只是忽略它并调用 inout
版本。有什么方法可以解决这个问题,而无需为可变的 const
和 immutable
定义 opEquals
重载?
只需删除 inout
。编译器会自动为模板推断 const
等属性。
无论如何,您并未将 inout
用于其预期目的; inout
用于根据参数将可变、常量或不可变转换为函数的 return 类型。函数体必须假设最坏的情况(const),所以它不能调用非常量方法。
请注意,由于在您的示例中 Test.opEquals
不是 const
,因此它只能在可变对象上调用。另请注意,在 D 中,const
是可传递的,因此 const(Wrapper!Test)
与 const(Wrapper!(const(Test)))
相同。所以无论如何,你不能在 const/immutable 对象上调用 Test.opEquals
(即使在包装器中),除非你使它成为 const
.
所有 inout
都是为了使 return 类型的常量可以匹配函数参数的常量。如果你有
const(Foo) bar(const(Foo) f) {
{
//...
return f;
}
然后你将一个 mutable
或 immutable
对象传递给 bar
,你最终得到一个 const
对象 returned,而如果你使用inout
inout(Foo) bar(inout(Foo) f) {
{
//...
return f;
}
return 类型与传递给 f
的参数具有相同的常量性。无论哪种方式,在函数内,f
都被有效地视为 const
。您只能在其上调用 const
和 inout
函数。因此,使 opEquals
inout
毫无意义,因为它没有 return 任何参数。和制作const
.
这里的根本问题是您试图在 const
对象上调用可变函数。这是不合法的,因为它违反了 const
。您有两个选项之一:
使
Wrapper
的opEquals
可变。然后它可以在T
的opEquals
可变时调用opEquals
。根据
T
如何定义opEquals
,使用static if
定义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
会在其函数上进行推断,而[=17则没有属性推断=]、inout
或 immutable
.