www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - const and immutable values, D vs C++?

reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
When is there a noticable difference when using const values 
instead of immutable values in a function body? And when should 
immutable be used instead of const?

f(){
   const x = g();
   immutable y = g();
   ... do stuff with x and y …
}

I'm comparing D to C++ and I get the following mapping:

D:
enum constant = number

C++:
enum : decltype(number) { constant = number }

D:
auto p =  g()

C++:
auto p = g()

D:
const p = g()

C++:
const auto p = g()

D:
immutable p = g()

C++:
hmmm...

Has anyone done a feature by feature comparison with C++? It 
would be interesting to see what it looks like.
Dec 04 2019
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
Also, in file scope, would C++:

constexpr auto constant = 3;

be the same as:

immutable constant = 3;

?
Dec 04 2019
prev sibling next sibling parent reply kerdemdemir <kerdemdemir gmail.com> writes:
On Wednesday, 4 December 2019 at 14:44:43 UTC, Ola Fosheim 
Grøstad wrote:
 When is there a noticable difference when using const values 
 instead of immutable values in a function body? And when should 
 immutable be used instead of const?

 f(){
   const x = g();
   immutable y = g();
   ... do stuff with x and y …
 }

 I'm comparing D to C++ and I get the following mapping:

 D:
 enum constant = number

 C++:
 enum : decltype(number) { constant = number }

 D:
 auto p =  g()

 C++:
 auto p = g()

 D:
 const p = g()

 C++:
 const auto p = g()

 D:
 immutable p = g()

 C++:
 hmmm...

 Has anyone done a feature by feature comparison with C++? It 
 would be interesting to see what it looks like.
Unfortunately I am not yet good with D to answer your question . But Ali Çehreli made some comparesions with C++. https://dconf.org/2013/talks/cehreli.pdf And I think you will find the answers of your questions in it also.
Dec 04 2019
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Wednesday, 4 December 2019 at 22:29:13 UTC, kerdemdemir wrote:
 Unfortunately I am not yet good with D to answer your question .
 But Ali Çehreli made some comparesions with C++.
 https://dconf.org/2013/talks/cehreli.pdf
 And I think you will find the answers of your questions in it 
 also.
Thanks. He didn't really compare to modern C++, but I appreciate the pointer. Seems to me that immutable references are primarily useful when calling a function with two references to an array. So you can be certain that the read-only reference will not be modified within the function (if both parameters point to the same array as they can with a const reference). Also, constexpr in C++ is a CTFE constraint and not a type, so not fully comparable to immutable, but same effect... perhaps. Not sure how smart compilers are in this regard. So immutable references i D is basically the same as const-references with restrict in C/C++ (non-standard, but common C++)?
Dec 04 2019
prev sibling parent reply Bastiaan Veelo <Bastiaan Veelo.net> writes:
On Wednesday, 4 December 2019 at 14:44:43 UTC, Ola Fosheim 
Grøstad wrote:
 When is there a noticable difference when using const values 
 instead of immutable values in a function body? And when should 
 immutable be used instead of const?

 f(){
   const x = g();
   immutable y = g();
   ... do stuff with x and y …
 }
There is a difference I guess if g() returns a reference type and is an inout function. immutable y will only work if the reference returned is immutable. Const is a promise to the rest of the code that you will never mutate it. Immutable is a promise by the rest of the code that it will never mutate. Immutable is more powerful, allowing data sharing in overlapping slices and between threads without locks. Const is more versatile, allowing references to data regardless of its mutability. So if g() always returns immutable, it’s best to receive it as such, not const. If it can be either, it must be received as const.
 I'm comparing D to C++ and I get the following mapping:
Does that make sense at all? D’s const is transitive, C++’s is not. Bastiaan.
Dec 04 2019
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Wednesday, 4 December 2019 at 22:43:35 UTC, Bastiaan Veelo 
wrote:
 There is a difference I guess if g() returns a reference type 
 and is an inout function. immutable y will only work if the 
 reference returned is immutable.
But not for values?
 Const is a promise to the rest of the code that you will never 
 mutate it. Immutable is a promise by the rest of the code that 
 it will never mutate.
But if it isn't marked as "shared" then only the current thread will modify it, so it is only different if you have a mutable reference as well that could modify the same object as a const reference.
 So if g() always returns immutable, it’s best to receive it as 
 such, not const. If it can be either, it must be received as 
 const.
Is there a way to specify in generic code that you want the best fit of a const/immutable reference depending on the return type (but not a mutable one)?
 I'm comparing D to C++ and I get the following mapping:
Does that make sense at all? D’s const is transitive, C++’s is not.
Yes, but it is the same for value types.
Dec 04 2019
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Wednesday, 4 December 2019 at 22:51:01 UTC, Ola Fosheim 
Grøstad wrote:
 Is there a way to specify in generic code that you want the 
 best fit of a const/immutable reference depending on the return 
 type (but not a mutable one)?
I mean, if f() return mutable or const then it should be const, but if it returns immutable then it should be immutable. Something like: readonly myref = f()
Dec 04 2019
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/4/19 5:57 PM, Ola Fosheim Grøstad wrote:
 On Wednesday, 4 December 2019 at 22:51:01 UTC, Ola Fosheim Grøstad wrote:
 Is there a way to specify in generic code that you want the best fit 
 of a const/immutable reference depending on the return type (but not a 
 mutable one)?
I mean, if f() return mutable or const then it should be const, but if it returns immutable then it should be immutable. Something like: readonly myref = f()
void foo(alias f)() { alias ConstType = const(typeof(f())); pragma(msg, ConstType); } const(int) a() { return 1; } immutable(int) b() { return 1; } int c() { return 1; } void main() { foo!a(); // const(int) foo!b(); // immutable(int) foo!c(); // const(int) } -Steve
Dec 04 2019
next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Wednesday, 4 December 2019 at 23:27:49 UTC, Steven 
Schveighoffer wrote:
 void main()
 {
     foo!a(); // const(int)
     foo!b(); // immutable(int)
     foo!c(); // const(int)
 }
Ok, so one has to use a wrapper and then "catch" the result with auto? auto x = foo!f();
Dec 04 2019
parent reply Ola Fosheim Grostad <ola.fosheim.grostad gmail.com> writes:
On Thursday, 5 December 2019 at 00:05:26 UTC, Ola Fosheim Grøstad 
wrote:
 On Wednesday, 4 December 2019 at 23:27:49 UTC, Steven 
 Schveighoffer wrote:
 void main()
 {
     foo!a(); // const(int)
     foo!b(); // immutable(int)
     foo!c(); // const(int)
 }
Ok, so one has to use a wrapper and then "catch" the result with auto? auto x = foo!f();
Nevermind...
Dec 04 2019
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
I haven't used const much in the past because I got burned on 
transitive const, so I managed to confuse myself.  I am not 
really interested in const/immutabel references here, but values. 
Seems like there is no reason to ever use const values, except 
when they contain a pointer to something non-immutable?

The only difference, as far as I can tell, is if the value type 
has pointers to other objects, immutable requires them to be to 
immutable objects. Which is a bit odd when you think of it. It 
basically means that you cannot put pointers to memory registers 
in ROM in a type safe manner... :-/ . That is a very odd 
restriction, because that is something you might want to do...

Seems to me that immutable should allow pointers to mutable 
memory and that const really shouldn't have been applicable to 
values, but only to pointers.

Anyway seems the rule of thumb is to:

- always use immutable on values
- except when the value might contain a pointer to non-immutable

Hm.
Dec 05 2019
prev sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
I haven't used const much in the past because I got burned on 
transitive const, so I managed to confuse myself.  I am not 
really interested in const/immutabel references here, only 
values. Seems like there is no reason to ever use const values, 
except when the value may contain a pointer to something 
non-immutable?

The only difference, as far as I can tell, is if the value type 
has pointers to other objects, immutable requires them to be to 
immutable objects. Which is a bit odd when you think of it. It 
basically means that you cannot put pointers to memory registers 
in ROM in a type safe manner... :-/ . That is a very odd 
restriction, because that is something you might want to do...

Seems to me that immutable should allow pointers to mutable 
memory and that const really shouldn't have been applicable to 
values, but only to pointers.

Anyway seems the rule of thumb would be:

- always use immutable on values
- except when the value might contain a pointer to non-immutable

Hm.
Dec 05 2019
prev sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Wednesday, 4 December 2019 at 23:27:49 UTC, Steven 
Schveighoffer wrote:
 void foo(alias f)()
 {
    alias ConstType = const(typeof(f()));
    pragma(msg, ConstType);
 }
I expressed myself poorly, what I am interested in is this: struct node1 {int value; const(node1)* next;} struct node2 {int value; immutable(node2)* next;} alias node = node1; // may swap to node2 node f(){ node tmp = {2019, null}; return tmp; } void main() { // choose readonly as immutable if x can be cast as such // otherwise choose const readonly x = f(); }
Dec 05 2019
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
I also find the following behaviour a bit unclear:

struct node0 {int value; node0* next;}
struct node1 {int value; const(node1)* next;}
struct node2 {int value; immutable(node0)* next;}

T mk(T)(){
     T tmp = {2019, null};
     return tmp;
}

node1 mk_node1(){
     node1 tmp = {2019, null};
     return tmp;
}

node2 mk_node2(){
     node2 tmp = {2019, null};
     return tmp;
}


void main() {
     immutable x0 = mk!node0();  //succeeds?
     immutable x1 = mk!node1();  //succeeds?
     immutable x2 = mk!node2();  //succeeds?
     immutable y1 = mk_node1();  //fails
     immutable y2 = mk_node2();  //succeeds, so only first child 
has to be immutable?
}
Dec 05 2019
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 5 December 2019 at 10:41:24 UTC, Ola Fosheim Grøstad 
wrote:
     immutable x1 = mk!node1();  //succeeds?
     immutable y1 = mk_node1();  //fails
Nevermind, seems like templated functions get stronger coercion, like: immutable y1 = cast(immutable)mk_node1(); (Also no need to explain that immutable(node0) rewrites all the pointer types to immutable, I get it :-)
Dec 05 2019
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
So basically, templated functions get flow-typing. I guess that 
is a good reason to use templated functions more...

What is the downside? E.g.:

struct node {int value; node* next;}

node mk_node2()(){
     node tmp = {2019, null};
     return tmp;
}

node mk_node(){
     node tmp = {2019, null};
     return tmp;
}

void main() {
	immutable x = mk_node2(); //succeeds
     immutable y = mk_node(); //fails
}
Dec 05 2019
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 5 December 2019 at 12:00:23 UTC, Ola Fosheim Grøstad 
wrote:
 So basically, templated functions get flow-typing.
But it is not reliable :-( struct node {int value; node* next;} node n0 = {1,null}; node mk_node1()(node* n){ node tmp = {2019, n}; return tmp; } node mk_node2()(bool ok){ node tmp = {2019, ok ? null : &n0}; return tmp; } void main() { immutable y = mk_node1(null); //succeeds immutable x = mk_node2(true); //fails }
Dec 05 2019