D 逃离;“我没有地狱”;
我有一些非常简单的代码拒绝编译:D 逃离;“我没有地狱”;,d,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
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
用于其预期目的;用于根据函数的参数将mutable、const或immutable传输到函数的返回类型。函数体必须假设最坏(const),因此不能调用非常量方法
请注意,在您的示例中,
Test.opEquals
不是const
,因此只能对可变对象调用它。还要注意,在D中,const
是可传递的,因此const(Wrapper!Test)
与const(Wrapper!(const(Test)))是相同的。因此,不管怎样,都不能对常量/不可变对象(即使在包装器中)调用Test.opEquals
,除非将其设置为const
所有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
。您有两种选择之一:
使Wrapper
的opEquals
可变。然后,当T
的opEquals
是可变的时,它可以调用opEquals
根据T
定义opEquals
的方式,使用static if
以不同方式定义opEquals
如果不使用static if
显式执行,则无法将opEquals
的常量从T
转发到Wrapper
。e、 g
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
或不可变的Wrapper
中的第二个opEquals
方法拼写错误。另外,因为opEquals
不修改它的参数,您可以将它设置为const
,并且它可以在可变和不可变类型上工作。我认为问题在于我将Wrapper.opEquals
标记为inout
,而不是将参数标记为inout
。从方法中删除inout
会导致它编译,但是如果我创建了一个不可变的或常量
包装器
,它将不起作用。如果您创建opEquals
常量,它将起作用,您无论如何都应该这样做,因为opEquals
不应该修改任何内容。您的示例缺少了一个关键部分:一个用户定义的类型,带有精心设计的opEquals
。查看我的修改:您可能需要使用static if
来查看包装类型的opEquals
是否为const,并在此基础上定义一个可变或常量opEquals
(inout更多的是将const/immutable转换为返回类型,布尔型不需要)。或使测试的opEquals
const,或使用别名t this
我无法从opEquals
中删除inout
,因为这样它将无法处理不可变的或常量
对象。让我们往下看const
,这样我可以更好地说明我的问题。看看,你明白为什么它不能编译,为什么我不能从包装器中删除inout
?@Meta它不能编译,因为Test.opEquals
不是const
。没有包装也不行<代码>断言(const(Test)(==const(Test)()
是的,所以我们回到我原来的问题。我想这意味着答案是“不”;您试图对常量对象调用Test.opEquals
,但Test.opEquals
不是常量。它与您的包装器或inout
无关。我的困惑源于错误的假设,即bool opEquals inout
应该在wrapper
是可变的情况下充当可变的。我想我现在已经很好地理解了为什么这样做行不通。。。如果你想让你的类型正确地处理任何“constness”,这真的是一个很大的难题。@Meta是的,一种向前处理constness的方法是有用的,但它到底应该如何工作成了一个有趣的问题。以前已经讨论过了,但还没有讨论过。幸运的是,它只在包装类型时才会出现,这主要发生在范围上,范围和“const”的交互非常糟糕,以至于像front
和empty
这样的函数缺少const
实际上并不重要。