digitalmars.D.learn - More TDPL overloads
- Andrej Mitrovic (45/45) Aug 25 2010 Page 373 adds another operator overload for use with integral numbers (C...
- Steven Schveighoffer (16/74) Aug 25 2010 This is an issue with template overloading.
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
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