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