std.typecons rebindable + tuple with const class gives warning
tsbockman
thomas.bockman at gmail.com
Thu Feb 4 20:40:43 UTC 2021
On Thursday, 4 February 2021 at 08:16:06 UTC, Saurabh Das wrote:
> This code:
>
> void main()
> {
> import std.typecons : rebindable, tuple;
> const c = new C();
> auto t = tuple(c.rebindable);
> }
>
> class C
> {
> }
>
> When compiled with DMD 2.095.0 gives a warning:
>
> Warning: struct Rebindable has method toHash, however it cannot
> be called with const(Rebindable!(const(C))) this.
>
> What is causing this? How can this warning be resolved?
`Rebindable!(C).toHash` forwards to `C.toHash`, which is
inherited from `Object.toHash`, which has the type: `nothrow
@trusted ulong()` according to pragma(msg,
typeof(Object.toHash));` That type signature forbids calling
`Object.toHash` on a mutable object. (An example of why this
might be a valid design choice, would be if computing the hash is
expensive and so the result is cached, which requires the freedom
to mutate the instance.)
To fix this problem, you need to do at least one of the following:
1) Make `c` mutable by declaring it with `auto` or `C` instead of
`const`. This is the only option if you cannot change the
definition of `C`.
2) If you can change `C`, you can override `toHash` in `C` with a
signature and implementation that do not require a mutable
object. Examples:
A) If you don't need associative array support from `C` or
its descendants at all, simply define `C.toHash` with more
permissive (to the caller) attributes, and crash if it gets
called unexpectedly:
class C
{
override size_t toHash() scope const pure @safe nothrow @nogc
{
assert(0, "Not implemented!");
}
}
B) If you want to support associative arrays by treating
every instance of `C` as a unique value:
class C
{
override size_t toHash() scope const pure @safe nothrow @nogc
{
static assert(C.sizeof == size_t.sizeof);
union Bits
{
const(C) self;
const(size_t) hash;
}
return Bits(this).hash;
}
// opEquals must always be defined such that is consistent
with toHash, such that this passes:
// if(this.opEquals(that))
// assert(this.toHash() == that.toHash());
override bool opEquals(Object that) scope const pure @safe
nothrow @nogc
{
return (this is that);
}
bool opEquals(scope const(C) that) scope const pure @safe
nothrow @nogc
{
return (this is that);
}
}
C) If you want to support associative arrays by treating
separate instances of `C` as equal based on the contents of their
data fields, then you will need to define appropriate `toHash`
and `opEquals` implementations:
class Point
{
int x, y;
override size_t toHash() scope const pure @safe nothrow @nogc
{
return size_t(x) * size_t(y);
}
override bool opEquals(Object that) scope const pure @safe
nothrow @nogc
{
if(auto thatPoint = cast(Point) that)
return opEquals(thatPoint);
else
return false;
}
bool opEquals(scope const(Point) that) scope const pure @safe
nothrow @nogc
{
return (this.x == that.x) && (this.y == that.y);
}
}
TLDR; Either make `c` mutable, or override/overload the `C`
associative array support methods `toHash` and `opEquals` to
support `const(C)` objects.
More information about the Digitalmars-d-learn
mailing list