copy must be const?!?

Jonathan M Davis newsgroup.d at jmdavisprog.com
Thu Jul 25 08:42:29 UTC 2024


On Thursday, July 25, 2024 12:50:04 AM MDT Dom DiSc via Digitalmars-d-learn 
wrote:
> On Wednesday, 24 July 2024 at 15:40:28 UTC, Dennis wrote:
> >> Is there a way to tell the compiler that it should discard
> >> "const" and "immutable" if it needs to create a copy?
> >> Unqual!T doesn't work :-(
> >
> > When you add `const` or `immutable` before the template type
> > parameter, it will infer T as simply `int`:
> >
> > ```D
> > T test2(T)(immutable T x) { return --T(x); }
> > ```
>
> Woah. Is this @safe?
> Ok, I know this is a fresh copy, that hopefully doesn't go to
> non-volatile memory, but I'm always sceptical casting away const.

It's not a cast. Casts in D use the keyword, cast - e.g.

    return --(cast(T)x);

Rather, Dennis' solution is constructing a value of the given type. For it
to work, T must be constructible from an immutable T - which works with
integer types but won't actually work with most types. It also won't work
with pointer types or reference types, since those would need new when
constructing them.

And no, in general, you don't want to be casting away const or immutable.
There are cases where it can work (e.g. if the cast does a copy, which it
would with an integer type), but if you ever cast away const or immutable
and mutate the result when the result isn't a fully independent copy, you're
violating the type system.

Usually, in cases like this one, you'd just require that the caller pass a
type that's mutable, forcing the caller to do whatever is appropriate to get
a mutable copy rather than trying to do such a copy internally, but Dennis'
solution will work with some types. That could be done by just letting the
caller get an error (like you were), or the more user-friendly solution is
to use a template constraint that checks that the operations that you need
to use actually work with the given type, e.g.

T test2(T)(T x)
    if(is(typeof(--x)))
{
    return --x;
}

So, then you get an error about the template constraint failing rather than
the template simply not being able to be instantiated. And aside from the
issue of const, that's arguably a better solution anyway, since then it will
catch any type which doesn't work with the decrement operator.

Then if you wanted to pass a const int, some of the options would be

test2(int(i));

test2(cast(int)(i));

test2!int(i); // works, because the compiler will copy i

In general though, you can't currently pass a const type to a templated
function and then have it instantiated without const. The only types that
that currently works with are dynamic arrays. Templated functions which take
dynamic arrays are instantiated with the same type you get when slicing
them, which means that const gets removed from the array itself but not from
the element type, e.g.

const int[] arr;
static assert(is(typeof(arr) == const(int[])));
static assert(is(typeof(arr[]) == const(int)[]));

So, if you have something like

immutable string foo = "hello";

and you pass it to a templated function, the type will be treated as string,
whereas with a user-defined type - or with any built-in types which aren't
dynamic arrays - the templated function is instantiated with exactly the
type that you pass it.

- Jonathan M Davis





More information about the Digitalmars-d-learn mailing list