Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User-defined implicit conversion of overloaded operator's arguments

Tags:

c#

The following operator overload is defined inside my Term class:

public static Term operator *(int c, Term t) {...}

This class also defines an implicit conversion from a Variable to Term:

public static implicit operator Term(Variable var) {...}

I would like to understand why the following does not compile:

static void Main(string[] args)
{
    Variable var = ...; // the details don't matter
    Console.WriteLine(2 * var); // var isn't implicitly converted to Term...
    Console.ReadKey();
}

The compiler says:

Operator '*' cannot be applied to operands of type 'int' and 'OOSnake.Variable'

Why isn't my overload of operator * found?

EDIT: As per the suggestion in the comments, here is a small complete example that re-produces the error:

namespace Temp
{
    class A { 
    }
    class B
    {
        public static implicit operator B(A a) { return new B(); }
        public static B operator *(int c, B b) { return new B(); }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(2 * new A());
        }
    }
}
like image 744
AlwaysLearning Avatar asked Dec 08 '15 16:12

AlwaysLearning


2 Answers

Basically, operator overload resolution doesn't include implicit user-defined conversions in order to find the operators that could be applicable.

From section 7.3.4 of the C# 5 specification:

An operation of the form x op y, where op is an overloadable binary operator, x is an expression of type X, and y is an expression of type Y, is processed as follows:

  • The set of candidate user-defined operators provided by X and Y for the operation operator op(x, y) is determined. The set consists of the union of the candidate operators provided by X and the candidate operators provided by Y, each determined using the rules of §7.3.5. If X and Y are the same type, or if X and Y are derived from a common base type, then shared candidate operators only occur in the combined set once.

And 7.3.5 doesn't include implicit user-defined conversions in its search for a set of operators.

Note that this also wouldn't work if the implicit conversion to Term was declared in the Variable class - although that would be more reasonable to specify and implement, as the compiler could look at the set of conversions from the operand type to other types, and use those for overload resolution.

However, this is only a matter of looking for the operators to start with. The compiler is happy to perform implicit conversions when it considers whether or not an overload is applicable. For example, in your case, if you add:

class A
{
    public static B operator *(A a, B b) { return new B(); }
}

Then this is valid:

A a = new A();        
Console.WriteLine(a * a);
like image 59
Jon Skeet Avatar answered Nov 15 '22 05:11

Jon Skeet


Console.WriteLine(2 * var) doesn't contain any hint that you would want to convert var to type Term. The compiler sees the int and multiplication operator and a variable of type 'Variable'.

Edit: To clarify, in order for your example to work the compiler would have to go through all the types in scope and see if one just happens to have an implicit conversion from type 'A'.

And if there also happens do be a class C like:

class C
{
    public static implicit operator C(A a) { return new A(); }
    public static B operator *(int i, C c) { return new C(); }
}

there's no telling what would happen.

Which is why the compiler doesn't do that :)

like image 45
Mark Avatar answered Nov 15 '22 03:11

Mark