www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - `this` template params for struct not expressing constness.

reply adnan338 <relay.dev.adnan protonmail.com> writes:
Hi, as far as I understand, the `this` template parameter 
includes constness qualifiers as seen in 
https://ddili.org/ders/d.en/templates_more.html

To apply this I have this following struct:

module bst;

struct Tree(T) {
     T item;
     Tree!T* parent, left, right;

     this(T item) {
         this.item = item;
     }

     Self* searchTree(this Self)(auto in ref T item) const {
         if (&this is null)
             return null;
         if (this.item == item)
             return &this;
         return (this.item < item) ? this.right.searchTree(item) : 
this.right.searchTree(item);
     }
}

unittest {
     auto text1 = "Hello", text2 = "World";

     auto tree2 = Tree!string(text1);
     assert(tree2.searchTree(text2) is null);
     assert(tree2.searchTree(text1) !is null);

     auto tree1 = Tree!int(4);
     assert(tree1.searchTree(5) is null);
     assert(tree1.searchTree(4) !is null);

}


When I run the unittest the compiler complains:

cannot implicitly convert expression &this of type 
const(Tree!string)* to Tree!string*

Run link: https://run.dlang.io/is/b76DND

Why does this happen?
Jun 08 2020
next sibling parent evilrat <evilrat666 gmail.com> writes:
On Monday, 8 June 2020 at 07:35:12 UTC, adnan338 wrote:
 Hi, as far as I understand, the `this` template parameter 
 includes constness qualifiers as seen in 
 https://ddili.org/ders/d.en/templates_more.html

 To apply this I have this following struct:

 module bst;

 struct Tree(T) {
     T item;
     Tree!T* parent, left, right;

     this(T item) {
         this.item = item;
     }

     Self* searchTree(this Self)(auto in ref T item) const {
         if (&this is null)
             return null;
         if (this.item == item)
             return &this;
         return (this.item < item) ? this.right.searchTree(item) 
 : this.right.searchTree(item);
     }
 }

 unittest {
     auto text1 = "Hello", text2 = "World";

     auto tree2 = Tree!string(text1);
     assert(tree2.searchTree(text2) is null);
     assert(tree2.searchTree(text1) !is null);

     auto tree1 = Tree!int(4);
     assert(tree1.searchTree(5) is null);
     assert(tree1.searchTree(4) !is null);

 }


 When I run the unittest the compiler complains:

 cannot implicitly convert expression &this of type 
 const(Tree!string)* to Tree!string*

 Run link: https://run.dlang.io/is/b76DND

 Why does this happen?
If I correctly understand what you are trying to do the answer is - in D const is transitive (unlike the C++ where it isn't). And in your searchTree() method you are basically trying to escape that constness. You can change the signature to return const(Self)*, or maybe add non-const overload depending on your needs. Don't even think about casting away const with `return cast(Self*) &this;` as this UB in D, and it will bite you somewhere later because compiler might rely on const for optimization.
Jun 08 2020
prev sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Monday, 8 June 2020 at 07:35:12 UTC, adnan338 wrote:
     Self* searchTree(this Self)(auto in ref T item) const {
         if (&this is null)
             return null;
         if (this.item == item)
             return &this;
         return (this.item < item) ?
             this.right.searchTree(item) :
             this.right.searchTree(item);
     }
This method is const, which means 'this' is const, while Self is not. What you're looking for here is inout (https://dlang.org/spec/function.html#inout-functions): auto searchTree()(auto in ref T item) inout { if (&this is null) return null; if (this.item == item) return &this; return (this.item < item) ? this.right.searchTree(item) : this.right.searchTree(item); } -- Simen
Jun 08 2020
parent reply adnan338 <relay.dev.adnan protonmail.com> writes:
On Monday, 8 June 2020 at 08:10:19 UTC, Simen Kjærås wrote:
 On Monday, 8 June 2020 at 07:35:12 UTC, adnan338 wrote:
     Self* searchTree(this Self)(auto in ref T item) const {
         if (&this is null)
             return null;
         if (this.item == item)
             return &this;
         return (this.item < item) ?
             this.right.searchTree(item) :
             this.right.searchTree(item);
     }
This method is const, which means 'this' is const, while Self is not. What you're looking for here is inout (https://dlang.org/spec/function.html#inout-functions): auto searchTree()(auto in ref T item) inout { if (&this is null) return null; if (this.item == item) return &this; return (this.item < item) ? this.right.searchTree(item) : this.right.searchTree(item); } -- Simen
Thank you. Few followup questions, if you don't mind. 1. What does that blank template parameter mean? 2. Since `inout` acts as a wildcard for immutable/const/non-const qualifiers, what should I do to have the compiler ensure that my method does not mutate a non-const tree inside the body?
Jun 08 2020
parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Monday, 8 June 2020 at 09:08:40 UTC, adnan338 wrote:
 On Monday, 8 June 2020 at 08:10:19 UTC, Simen Kjærås wrote:
 On Monday, 8 June 2020 at 07:35:12 UTC, adnan338 wrote:
     Self* searchTree(this Self)(auto in ref T item) const {
         if (&this is null)
             return null;
         if (this.item == item)
             return &this;
         return (this.item < item) ?
             this.right.searchTree(item) :
             this.right.searchTree(item);
     }
This method is const, which means 'this' is const, while Self is not. What you're looking for here is inout (https://dlang.org/spec/function.html#inout-functions): auto searchTree()(auto in ref T item) inout { if (&this is null) return null; if (this.item == item) return &this; return (this.item < item) ? this.right.searchTree(item) : this.right.searchTree(item); } -- Simen
Thank you. Few followup questions, if you don't mind. 1. What does that blank template parameter mean?
Just forces the function to be a template. The only reason for this is it's required for auto ref to work, which you may or may not need on that function.
 2. Since `inout` acts as a wildcard for 
 immutable/const/non-const qualifiers, what should I do to have 
 the compiler ensure that my method does not mutate a non-const 
 tree inside the body?
Inside inout functions, `this` is treated as const - any attempt to modify it should give a compile error. Since D const is transitive, anything reachable from `this` is also treated as const. If you're able to mutate a non-const tree inside the body, there's a bug in the compiler. -- Simen
Jun 08 2020