www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - copy must be const?!?

reply Dom DiSc <dominikus scherkl.de> writes:
In following code:
```d
int test(int x) { return --x; }
T test2(T)(T x) { return --x; }

void main()
{
    const int v = 3;
    writeln(test(v));
    writeln(test2(v)); // doesn't compile
}
```

test2 (just like test) works on a copy of x. Why is this copy 
const?!? If I copy a const or immutable object, most of the time 
I do so because I want to work with this value. Getting a const 
copy is never useful. If it were, why is it not const in test?

This also annoys me if I copy write protected files on windows - 
why on earth should the copy be write protected?

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 :-(
Jul 24
next sibling parent reply Dennis <dkorpel gmail.com> writes:
On Wednesday, 24 July 2024 at 15:08:28 UTC, Dom DiSc wrote:
 Why is this copy const?!? If I copy a const or immutable 
 object, most of the time I do so because I want to work with 
 this value.
Because the compiler instantiates `test2!T` deriving T from the type of the argument you pass to `x`, which is `const(int)`. This is indeed an annoyance with const, and there's even some special treatment for arrays such that passing a `const char[]` will instantiate with `const(char)[]`, which is necessary to make ranges work. There's a DIP trying to generalize this behavior to user-defined types, but it's WIP: https://forum.dlang.org/thread/ut4f3m$2e4a$1 digitalmars.com
 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); } ```
Jul 24
parent reply Dom DiSc <dominikus scherkl.de> writes:
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.
Jul 24
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Jul 25
parent reply Dom DiSc <dominikus scherkl.de> writes:
On Thursday, 25 July 2024 at 08:42:29 UTC, Jonathan M Davis wrote:
 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.
Ah! Good, so at least here I can use it. Fine. Should also wor if the type has an appropriate constructor, e.g. a copy constructor that takes a const parameter and returns a mutable copy (as I would expect every copy constructor to do), so at least in code I write, I will never have a problem.
 It also won't work with pointer types or reference types, since 
 those would need new when constructing them.
Of course. For those that also wouldn't work for functions instead of templates.
 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 a parameter given by value is ALWAYS a copy. So if a copy is done, the copy should be mutable. I can't see why that should ever be a problem. The compiler should never create new values const or immutable unless explicitly advised to do so. E.g. ```d void fun(myType x) { } ``` called with immutable object should create a mutable x, because ```d myType x = immutableObject; ``` will also create a mutable x. If I want an immutable x, i can do ```d immutable myType = immutableObject; ``` ```d void fun(T)(T x) if (is(Unqual!T==myType)) { } ``` should also create a mutable x, for the same reason, because if I want an immutable x, I can do ```d void fun(T)(immutable(T) x) if (is(Unqual!T==myType)) { } ``` Therefore I consider the current behaviour a bug.
Jul 25
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, July 25, 2024 6:00:58 AM MDT Dom DiSc via Digitalmars-d-learn 
wrote:
 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 a parameter given by value is ALWAYS a copy. So if a copy is done, the copy should be mutable. I can't see why that should ever be a problem. The compiler should never create new values const or immutable unless explicitly advised to do so.
It has to be a _full_, independent copy. If you're talking about integer types, that's a non-issue, but if you're talking about types with any indirections, it becomes a big issue. If the type contains any pointers, dynamic arrays, class references, etc. then the copy can't be mutable unless you're dealing with a struct with a copy constructor that does a deep copy of all of the member variables. So, in the general case, you cannot copy a value and expect to get a mutable result.
 E.g.
 ```d
 void fun(myType x) { }
 ```

 called with immutable object should create a mutable x, because

 ```d
    myType x = immutableObject;
 ```

 will also create a mutable x. If I want an immutable x, i can do

 ```d
    immutable myType = immutableObject;
 ```

 ```d
 void fun(T)(T x) if (is(Unqual!T==myType)) { }
 ```

 should also create a mutable x, for the same reason, because if I
 want an immutable x, I can do


 ```d
 void fun(T)(immutable(T) x) if (is(Unqual!T==myType)) { }
 ```

 Therefore I consider the current behaviour a bug.
It's most definitely not a bug that IFTI (Implicit Function Template Instantiation) instantiates the template with the exact type that it's given. In the general case, that's what you want - particularly since many, many types (probably the vast majority of types) cannot be const or immutable and then result in a mutable copy when they're copied. That really only works with very simple types with no indirections. In the general case, there is no expectation whatsoever that it's going to be possible to get a mutable copy from a const or immutable variable, and generic code that expects that to work is very likely to run into problems very quickly. Now, the fact that code such as void fun(T : const U, U)(U x) if(is(immutable T == immutable U)) { ... } or code like void fun(T)(Unqual!T x) { } doesn't work with IFTI and requires explicit instantation is definitely a deficiency in IFTI's current capabilities, but in the general case, we very much want void fun(T)(T x) {} to be instantiated as fun!(const Foo) if it's passed a const Foo. For some types, we _would_ like the ability to tell the compiler to implicitly instantiate function templates with a tail-const version similar to what we get with dynamic arrays, but that really only makes sense for certain types such as ranges. But either way, it would cause quite a few problems if IFTI started instantiating templates in general with mutable instead of the actual type that it's given, because it's extremely common that const and immutable types cannot be converted to mutable. - Jonathan M Davis
Jul 25
next sibling parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Thursday, 25 July 2024 at 13:07:03 UTC, Jonathan M Davis wrote:
 On Thursday, July 25, 2024 6:00:58 AM MDT Dom DiSc via 
 Digitalmars-d-learn wrote:
 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 a parameter given by value is ALWAYS a copy. So if a copy is done, the copy should be mutable. I can't see why that should ever be a problem. The compiler should never create new values const or immutable unless explicitly advised to do so.
It has to be a _full_, independent copy. If you're talking about integer types, that's a non-issue, but if you're talking about types with any indirections, it becomes a big issue. If the type contains any pointers, dynamic arrays, class references, etc. then the copy can't be mutable unless you're dealing with a struct with a copy constructor that does a deep copy of all of the member variables. So, in the general case, you cannot copy a value and expect to get a mutable result.
 E.g.
 ```d
 void fun(myType x) { }
 ```

 called with immutable object should create a mutable x, because

 ```d
    myType x = immutableObject;
 ```

 will also create a mutable x. If I want an immutable x, i can 
 do

 ```d
    immutable myType = immutableObject;
 ```

 ```d
 void fun(T)(T x) if (is(Unqual!T==myType)) { }
 ```

 should also create a mutable x, for the same reason, because 
 if I want an immutable x, I can do


 ```d
 void fun(T)(immutable(T) x) if (is(Unqual!T==myType)) { }
 ```

 Therefore I consider the current behaviour a bug.
It's most definitely not a bug that IFTI (Implicit Function Template Instantiation) instantiates the template with the exact type that it's given. In the general case, that's what you want - particularly since many, many types (probably the vast majority of types) cannot be const or immutable and then result in a mutable copy when they're copied. That really only works with very simple types with no indirections. In the general case, there is no expectation whatsoever that it's going to be possible to get a mutable copy from a const or immutable variable, and generic code that expects that to work is very likely to run into problems very quickly. Now, the fact that code such as ```d void fun(T : const U, U)(U x) if(is(immutable T == immutable U)) { ... } ``` or code like ```d void fun(T)(Unqual!T x) { } ``` doesn't work with IFTI and requires explicit instantation is definitely a deficiency in IFTI's current capabilities, but in the general case, we very much want ```d void fun(T)(T x) {} ``` to be instantiated as `fun!(const Foo)` if it's passed a `const Foo`. For some types, we _would_ like the ability to tell the compiler to implicitly instantiate function templates with a tail-const version similar to what we get with dynamic arrays, but that really only makes sense for certain types such as ranges. But either way, it would cause quite a few problems if IFTI started instantiating templates in general with mutable instead of the actual type that it's given, because it's extremely common that const and immutable types cannot be converted to mutable.
It’s wild how C++ and D disagree on this one. In C++, a copy constructor must produce a mutable copy. Now, C++ and D disagree on what `const` means and therefore what counts as a mutable copy. But, and this is the key takeaway, C++ never infers `const` on a copy. Never. It does infer `const` on a reference, of course. Nothing of this is out of reach for D. A type for which const objects can’t be copied to initialize a mutable one simply isn’t copyable and therefore can’t be passed by value. A type for which const rvalues can’t be used to initialize a mutable variable aren’t movable. C++ solves this by binding values by universal reference: ```cpp template<typename T> void f(T&&); ``` This `f` eats anything. For rvalues of type `X`, `T` is inferred `X`, the rvalue is materialized in the caller and passed by (rvalue) reference to `f`. For lvalues, `T` is inferred `X&` so that the parameter becomes of type `(T&)&&` which is the same as `T&`, thus `f` ends up binding the value by (lvalue) reference. D doesn’t have that. In that regard, D is approximately where C++ was before C++11. D’s `ref` can’t bind lvalues, which is probably a good thing. In one of my recent DIP Ideas posts, I outlined ` universal ref` and ` rvalue ref`. The best D can do today is this: ```d void f(T)(auto ref T); ``` For lvalues, that’s great. Nothing to complain. Rvalues should be moved into the parameter. For that, the type must support moves, which most types do, but the compiler doesn’t check if the move could give you a mutable object. Even if it can, the compiler will give you a qualified parameter. Because of D’s transitive qualifiers, a mutable copy may not be possible. In that case, we’d have two options: Say the type isn’t copyable, or say “here’s your ‘copy,’ but it’s const.” However, copying a type for which there’s a copy constructor that can give you a mutable object is a no-no.
Jul 25
parent Dom DiSc <dominikus scherkl.de> writes:
On Thursday, 25 July 2024 at 22:22:19 UTC, Quirin Schroll wrote:
 On Thursday, 25 July 2024 at 13:07:03 UTC, Jonathan M Davis 
 wrote:
 But either way, it would cause quite a few problems if IFTI 
 started instantiating templates in general with mutable 
 instead of the actual type that it's given, because it's 
 extremely common that const and immutable types cannot be 
 converted to mutable.
It’s wild how C++ and D disagree on this one. In C++, a copy constructor must produce a mutable copy. Now, C++ and D disagree on what `const` means and therefore what counts as a mutable copy. But, and this is the key takeaway, C++ never infers `const` on a copy. Never. It does infer `const` on a reference, of course.
sigh. One more point where C++ made better decisions.
 Nothing of this is out of reach for D. A type for which const 
 objects can’t be copied to initialize a mutable one simply 
 isn’t copyable and therefore can’t be passed by value. A type 
 for which const rvalues can’t be used to initialize a mutable 
 variable aren’t movable.
+1
 However, copying a type [with const] for which there’s a copy 
 constructor that can give you a mutable object is a no-no.
That's my point.
Jul 26
prev sibling next sibling parent reply Andy Valencia <dont spam.me> writes:
On Thursday, 25 July 2024 at 13:07:03 UTC, Jonathan M Davis wrote:

 It's most definitely not a bug that IFTI (Implicit Function 
 Template Instantiation) instantiates the template with the 
 exact type that it's given.
The "principle of least astonishment" certainly supports this behavior. To check my understanding, I forced the type of instantiation: writeln(test2!int(v)); and that worked as I expected. Andy
Jul 25
parent Dom DiSc <dominikus scherkl.de> writes:
On Friday, 26 July 2024 at 02:34:12 UTC, Andy Valencia wrote:
 On Thursday, 25 July 2024 at 13:07:03 UTC, Jonathan M Davis 
 wrote:

 It's most definitely not a bug that IFTI (Implicit Function 
 Template Instantiation) instantiates the template with the 
 exact type that it's given.
The "principle of least astonishment" certainly supports this behavior. To check my understanding, I forced the type of instantiation: writeln(test2!int(v)); and that worked as I expected. Andy
Yes, it's pretty much the same "solution" but on the caller side instead of the callee. But it works only for types like int or which provides a constructor that generates a mutable copy.
Jul 26
prev sibling parent reply Dom DiSc <dominikus scherkl.de> writes:
On Thursday, 25 July 2024 at 13:07:03 UTC, Jonathan M Davis wrote:
 On Thursday, July 25, 2024 6:00:58 AM MDT Dom DiSc via
 But a parameter given by value is ALWAYS a copy.
It has to be a _full_, independent copy. If you're talking about integer types, that's a non-issue, but if you're talking about types with any indirections, it becomes a big issue. If the type contains any pointers, dynamic arrays, class references, etc. then the copy can't be mutable unless you're dealing with a struct with a copy constructor that does a deep copy of all of the member variables. So, in the general case, you cannot copy a value and expect to get a mutable result.
If you are not able to construct a mutable copy of a type, why on earth are you handing it over by value?!? And what do you do with normal assignment with such a type? ```d immutable MyNonCopyableType x; MyNonCopyableType y = x; // compile error? ``` Also, if you need no writeable copy in your template, why don't you declare it with const parameter? Maybe you need this behaviour often and so don't want to type the 7 extra characters. But I think, it is a bad default to create const copies of const objects, and to make it worse the language does not allow you to change this default (would need a new keyword "mutable" to explicitly request to get a mutable copy).
 Therefore I consider the current behaviour a bug.
It's most definitely not a bug that IFTI (Implicit Function Template Instantiation) instantiates the template with the exact type that it's given. In the general case, that's what you want - particularly since many, many types (probably the vast majority of types) cannot be const or immutable and then result in a mutable copy when they're copied.
If you need this, declare the parameter const. The default should be a mutable copy (and a compile error if creating a mutable copy is not possible).
 That really only works with very simple types with no 
 indirections.
Sorry, but no. It is possible to write copy constructors that create mutable copies for pretty much any type. It may for bad designed types be not very fast, but if you are able to create a mutable instance of a type, it should also be possible to create a mutable copy.
 Now, the fact that code such as
     ```
     void fun(T : const U, U)(U x)
         if(is(immutable T == immutable U))
     {
         ...
     }
     ```
 or code like
     ```
     void fun(T)(Unqual!T x) {}
     ```
 doesn't work with IFTI and requires explicit instantation is 
 definitely a deficiency in IFTI's current capabilities.
So you basically agree that it is a missing feature (if not a bug).
 we very much want
     ```
     void fun(T)(T x) {}
     ```
 to be instantiated as fun!(const Foo) if it's passed a const 
 Foo.
No. this is a bad default. If you want this, write ```d void fun(T)(const(T) x) {} ``` I know, it's seven characters more to type, but I think they are definitely worth it.
Jul 26
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Friday, July 26, 2024 2:17:21 AM MDT Dom DiSc via Digitalmars-d-learn 
wrote:
 On Thursday, 25 July 2024 at 13:07:03 UTC, Jonathan M Davis wrote:
 On Thursday, July 25, 2024 6:00:58 AM MDT Dom DiSc via

 But a parameter given by value is ALWAYS a copy.
It has to be a _full_, independent copy. If you're talking about integer types, that's a non-issue, but if you're talking about types with any indirections, it becomes a big issue. If the type contains any pointers, dynamic arrays, class references, etc. then the copy can't be mutable unless you're dealing with a struct with a copy constructor that does a deep copy of all of the member variables. So, in the general case, you cannot copy a value and expect to get a mutable result.
If you are not able to construct a mutable copy of a type, why on earth are you handing it over by value?!?
Why not? Structs are almost never reference types (since that would mean only holding a single pointer or class reference), but they're often pseudo-reference types. Something as simple as having a dynamic array or string in your struct means that you can't get a mutable copy without duplicating memory (and depending on the types involved, you can't even do that), but such types can often work just fine with const copies, because the data that's shared between objects is const or immutable.
 And what do you do with normal assignment with such a type?
You can't assign to variables which are const or immutable, so assignment won't work. But plenty of generic code is written which never assigns to a variable and works just fine with const objects being copied.
 ```d
 immutable MyNonCopyableType x;
 MyNonCopyableType y = x; // compile error?
 ```
If you can't get a mutable copy of MyNonCopyableType, then yes, you'll get a compiler error.
 Also, if you need no writeable copy in your template, why don't
 you declare it with const parameter?
 Maybe you need this behaviour often and so don't want to type the
 7 extra characters. But I think, it is a bad default to create
 const copies of const objects, and to make it worse the language
 does not allow you to change this default (would need a new
 keyword "mutable" to explicitly request to get a mutable copy).
In D, const is pretty much cancer, honestly. You do _not_ want to use it for generic code if you can possible avoid it, because many, many types do not work with it at all. Unlike C++'s const, it's transitive and has no backdoors (at least not without violating the type system), making it very easy to end up in situations where even when a type can be logically const (or which could be const in C++), it cannot be const in D, because D's const is simply too restrictive. When something is const in D, it means it. There are types when those guarantees are useful, but often, the end result is that you're better off avoding const with any user-defined types, because const is simply too restrictive. And if a type has any indirections in it, the odds are extremely low that it's going to be possible to get a mutable copy from a const object. As such, unless you're dealing with a specific subset of types where you know that const will work (e.g. if you're only dealing with integer types), then you usually don't want to use const at all with templates. Templates will often work with const types just fine when those types are designed to work with const, but if the template require const, then you're going to end up with a lot of types that won't work with that template, because they won't work with const (and in many cases, cannot be made to work with const).
 It's most definitely not a bug that IFTI (Implicit Function
 Template Instantiation) instantiates the template with the
 exact type that it's given. In the general case, that's what
 you want - particularly since many, many types (probably the
 vast majority of types) cannot be const or immutable and then
 result in a mutable copy when they're copied.
If you need this, declare the parameter const. The default should be a mutable copy (and a compile error if creating a mutable copy is not possible).
If you declare the parameter to be const, then there a lot of types that won't work with the template. D's const is just too restrictive for it to make any sense to use it unless you're specifically restricting your code to a subset of types which are designed to work with it. With generic code, it's usually the case that the decision on whether const should be used needs to be left up to the caller if you want any chance of the code working with a large range of types. Using const parameters with function templates can work quite well with the primitive types, but with user-defined types, it tends to become untenable quite quickly - especially if you're writing library code that will be used by a whole bunch of people writing types that you don't control.
 That really only works with very simple types with no
 indirections.
Sorry, but no. It is possible to write copy constructors that create mutable copies for pretty much any type. It may for bad designed types be not very fast, but if you are able to create a mutable instance of a type, it should also be possible to create a mutable copy.
Given how D's const works, that really isn't true - especially when you're dealing with any types that you don't control the definition of. For there to be any hope of it working in the general case to get a mutable copy of a const object, D structs in general would have to be written in a way that that was true, and copying structs would become far more expensive, because it would require copying memory all over the place. That's simply not how typical D code is written. And copy constructors are actually fairly rare in D code. They certainly exist, but since structs are passed around by value, and the way that dynamic arrays work makes it _extremely_ common to share data between objects, it's simply not typical to write copy constructors that do stuff like duplicate memory - which is what would often be required to get a mutable copy from a const object. On top of that, in many cases, getting a mutable copy would give you completely incorrect behavior - e.g. if a range is a range over a container, you need that range to point to the elements in the container, and if the range refers to those elements as const, then getting a mutable copy of those elements would mean that they're no longer necessarily the same elements which are in the container (they might be at the point that the copy is made, but that won't necessarily stay true, whereas it would if the elements aren't copied). It's also the case that a lot of code simply avoids using const altogether with user-defined types because of how restrictive D's const is. So, even when types have copy constructors, they're often not written with the idea of getting a mutable copy from a const object. It's something that will work with some types to be sure, but for many types, it won't. And while you can certainly consider that a deficiency in common D programming practices, it's in large part the result of how restrictive D's const is. Most anyone who tries to use D's const like you would in C++ eventually stops, because D's const is simply too different from C++'s const for that to work. And that's especially true with templated code, since if it has to work with a large range of types, types which don't work with const are going to be on the list. On top of that, some common D idioms (e.g. ranges) can't work with const, because they require mutation (a range can have const elements, but the range itself cannot be const). And because there is no requirement that a const object of any of these types be able to be copied to get a mutable copy, and that would often be expensive, most types aren't written that way. So, in general, it just works better to not use const with templated code and leave that decision up to the caller. While you might find the current situation annoying with the use case that you're dealing with at the moment, ultimately, D templates work with _far_ more types without mucking around with the mutabality of the arguments than they would if the template changed the mutability by default, and most of the time, you really don't want to use const with them anyway, because it's simply not going to work with a lot user-defined types. - Jonathan M Davis
Jul 26
parent Dom DiSc <dominikus scherkl.de> writes:
On Friday, 26 July 2024 at 10:04:45 UTC, Jonathan M Davis wrote:
 On Friday, July 26, 2024 2:17:21 AM MDT Dom DiSc via 
 Digitalmars-d-learn wrote:
 If you are not able to construct a mutable copy of a type, why 
 on earth are you handing it over by value?!?
Why not?
Because you can't make a mutable copy?
 Structs are [...] often pseudo-reference types. [...]
 such types can often work just fine with const copies, because 
 the data that's shared between objects is const or immutable.
Then declare the template to take a const parameter.
 ```d
 immutable MyNonCopyableType x;
 MyNonCopyableType y = x; // compile error?
 ```
If you can't get a mutable copy of MyNonCopyableType, then yes, you'll get a compiler error.
This is why you should get the same error, if the compiler cannot create a mutable parameter-by-value copy.
 In D, const is pretty much cancer, honestly. You do _not_ want 
 to use it for generic code if you can possible avoid it,
Why? In a template that guarantees not to modify a parameter by declaring it const, what bad can happen?
 because many, many types do not work with it at all.
At least reading it should never be a problem. And nothing more one want to do with a const parameter. But I don't see your problem. I'm taking about situations where a const instance of a type IS ALREADY THERE (which will never be the case with any of your problematic types), and I want to have a mutable copy of them. So if the compiler would discard the const from parameters that are anyway never const, that's a NOP. All is fine. On the other hand if there is a const object, that should be an object of a type that indeed CAN provide mutable copies. This is also fine. So, where is the problem if the compiler drops "const" from the by-value copy of a parameter?
 in situations where a type can be logically const (or which 
 could be const in C++), it cannot be const in D, because D's 
 const is simply too restrictive.
Sorry, but if a type is "logically const" (like a range) and you read it and thereby change it's internal state, then the template that guaranteed not to modify a parameter is very likely to destroy this internal state, and so you are much better of if this doesn't compile beforehand. Some templates are simply not meant to work with all kinds of user defined types.
 If you declare the parameter to be const, then there are a lot 
 of types that won't work with the template.
Fine. If I can change the internal state via read-functions, I don't want to work with that bogous type. If my intention is not to modify an object, I'm perfectly ok that I can't use may of that fake-const types functions.
 On top of that, in many cases, getting a mutable copy would 
 give you completely incorrect behavior - e.g. if a range is a 
 range over a container, you need that range to point to the 
 elements in the container, and if the range refers to those 
 elements as const, then getting a mutable copy of those 
 elements would mean that they're no longer necessarily the same 
 elements which are in the container (they might be at the point 
 that the copy is made, but that won't necessarily stay true, 
 whereas it would if the elements aren't copied).
Ranges are a bad example, because they really cannot be const. They are completely unusable in a function not modifying them, because reading them is using them up. So a function taking a range as parameter indeed cannot guarantee not to modify it (at least not if it reads that parameters data at all).
 It's also the case that a lot of code simply avoids using const 
 altogether with user-defined types
Fine. Then you won't run into the problem I have. And indeed, as you weren't aware of this problem is an indicator that you are not really using const.
 Most anyone who tries to use D's const like you would in C++ 
 eventually stops, because D's const is simply too different 
 from C++'s const for that to work.
No, so far the C++ code I converted to D is working fine. And it is heavily using const - but it uses it correct, despite C++ doesn't enforce that. Maybe I never used C++ the way it was meant?!?
 And that's especially true with templated code, since if it has 
 to work with a large range of types, types which don't work 
 with const are going to be on the list.
Nope. Constraints work very good in guaranteeing that such types are NOT on the list of types the template is working with.
 On top of that, some common D idioms (e.g. ranges) can't work 
 with const, because they require mutation (a range can have 
 const elements, but the range itself cannot be const).
Jup. If a template takes a range, it should not take it const. But I also won't take a range by value. As whatever I do will modify the range, why should I pretend to work on a copy of it? Take it by ref!
Jul 29
prev sibling parent swigy food <hamza1r3g3h gmail.com> writes:
Hello
A const copy ensures the copied value remains unchanged, 
providing safety and predictability. If the original is const, 
copying it as non-const could introduce unintended side effects. 
To modify a copied value, create a mutable copy explicitly.
For file systems, copying write-protected files maintains their 
attributes for security reasons. To modify them, change the file 
permissions after copying.
There isn't a direct way to tell the compiler to discard "const" 
or "immutable" attributes during a copy. You need to cast away 
constness explicitly where necessary.
Jul 30