www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - More TDPL overloads

reply Andrej Mitrovic <andrej.mitrovich name.com> writes:
Page 373 adds another operator overload for use with integral numbers
(CheckedInt op Int). But it conflicts with the previos template (CheckedInt op
CheckedInt):

module binary_ops;

import std.stdio : writeln;
import std.traits;
import std.exception;

unittest
{
    auto foo = CheckedInt!(int)(5);

    foo + 4;
}

void main()
{
}
 
struct CheckedInt(N) if (isIntegral!N)
{
    private N value;
    public int x;
    
    this(N value)
    {
        this.value = value;
    }
    
    // Operation with raw numbers
    CheckedInt opBinary(string op)(N rhs) if (isIntegral!N)
    {
        return opBinary!op(CheckedInt(rhs));
    }
    
    // addition
    CheckedInt opBinary(string op)(CheckedInt rhs) if (op == "+")
    {
        auto result = value + rhs.value;
        
        enforce(rhs.value >= 0 ? result >= value : result < value);
        return CheckedInt(result);
    }
}

I get back:

binary_ops.d(34): Error: template instance opBinary!(op) matches more than one
template declaration, 
binary_ops.d(32):opBinary(string op) if (isIntegral!(N)) and
binary_ops.d(38):opBinary(string op) if (op == "+")

One is taking an integral by using a constraint, the other specifically a
CheckedInt type. Any clues how they both match?

If I remove the first operator overload template then I can't compile, so
where's the ambiguity?
Aug 25 2010
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 25 Aug 2010 16:54:19 -0400, Andrej Mitrovic  
<andrej.mitrovich name.com> wrote:

 Page 373 adds another operator overload for use with integral numbers  
 (CheckedInt op Int). But it conflicts with the previos template  
 (CheckedInt op CheckedInt):

 module binary_ops;

 import std.stdio : writeln;
 import std.traits;
 import std.exception;

 unittest
 {
     auto foo = CheckedInt!(int)(5);

     foo + 4;
 }

 void main()
 {
 }
 struct CheckedInt(N) if (isIntegral!N)
 {
     private N value;
     public int x;
    this(N value)
     {
         this.value = value;
     }
    // Operation with raw numbers
     CheckedInt opBinary(string op)(N rhs) if (isIntegral!N)
     {
         return opBinary!op(CheckedInt(rhs));
     }
    // addition
     CheckedInt opBinary(string op)(CheckedInt rhs) if (op == "+")
     {
         auto result = value + rhs.value;
        enforce(rhs.value >= 0 ? result >= value : result < value);
         return CheckedInt(result);
     }
 }

 I get back:

 binary_ops.d(34): Error: template instance opBinary!(op) matches more  
 than one template declaration,
 binary_ops.d(32):opBinary(string op) if (isIntegral!(N)) and
 binary_ops.d(38):opBinary(string op) if (op == "+")

 One is taking an integral by using a constraint, the other specifically  
 a CheckedInt type. Any clues how they both match?

 If I remove the first operator overload template then I can't compile,  
 so where's the ambiguity?

This is an issue with template overloading. To the compiler, the template instantiations are identical, because opBinary(string op) only has one template parameter. The arguments to the function are only looked at afterwards for overloading. So the compiler wants to make a decision first of which *template* to instantiate, the first or the second, then looks at function overloading. I believe there's a bug on this in bugzilla already. It makes things more difficult for operator overloading. Also that first function signature is wrong. N is not a template parameter to the *function* and isIntegral!N is already checked on the struct, so there is no need to add that constraint again. So with the constraint removed, we can work around the problem by adding a superfluous template argument
    // Operation with raw numbers
     CheckedInt opBinary(string op, T)(T rhs) if (is(T == N))
     {
         return opBinary!op(CheckedInt(rhs));
     }
    // addition
     CheckedInt opBinary(string op, T)(T rhs) if (op == "+" && is(T ==  
 CheckedInt))
     {
         auto result = value + rhs.value;
        enforce(rhs.value >= 0 ? result >= value : result < value);
         return CheckedInt(result);
     }

-Steve
Aug 25 2010