User-defined implicit conversion of overloaded operator's arguments - 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());
}
}
}

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);

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 :)

If you want to make your example to work, you must move the implicit operator that converts A to B to the class A, like this:
namespace Temp
{
class A
{
public static implicit operator B(A a) { return new B(); }
}
class B
{
public static B operator *(int c, B b) { return new B(); }
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(2 * ((B)new A()));
}
}
}
But you cant use this 2 * new A() because(Jon Skeet answer):
Basically, operator overload resolution doesn't include implicit user-defined conversions in order to find the operators that could be applicable.
https://stackoverflow.com/a/34161798/815590

Related

C# Equality check for generic wrapper with implicit cast doesn't work for classes [duplicate]

Some code for context:
class a
{
}
class b
{
public a a{get;set;}
public static implicit operator a(b b)
{
return b.a;
}
}
a a=null;
b b=null;
a = b;
//compiler: cannot apply operator '==' to operands of type tralala...
bool c = a == b;
Is it possible to use == operator on different type instances, where one can implicitly convert to another? What did i miss?
Edit:
If types must be the same calling ==, then why
int a=1;
double b=1;
bool c=a==b;
works?
The implicit operator only works for assignment.
You want to overload the equality (==) operator, as such:
class a
{
public static bool operator ==(a x, b y)
{
return x == y.a;
}
public static bool operator !=(a x, b y)
{
return !(x == y);
}
}
class b
{
public a a{get;set;}
public static implicit operator a(b b)
{
return b.a;
}
}
This should then allow you to compare two objects of type a and b as suggested in your post.
var x = new a();
var y = new b();
bool c = (x == y); // compiles
Note:
I recommmend simply overriding the GetHashCode and Equals method, as the compiler warns, but as you seem to want to supress them, you can do that as follows.
Change your class declaration of a to:
#pragma warning disable 0660, 0661
class a
#pragma warning restore 0660, 0661
{
// ...
}
Is it possible to use == operator on
different type instances, where one
can implicitly convert to another?
Yes.
What did i miss?
Here's the relevant portion of the specification. You missed the highlighted word.
The predefined reference type equality
operators require [that] both operands
are reference-type values or the
literal null. Furthermore, a standard
implicit conversion exists from the
type of either operand to the type of
the other operand.
A user-defined conversion is by definition not a standard conversion. These are reference types. Therefore, the predefined reference type equality operator is not a candidate.
If types must be the same calling ==,
then why [double == int] works?
Your supposition that the types must be the same is incorrect. There is a standard implicit conversion from int to double and there is an equality operator that takes two doubles, so this works.
I think you also missed this bit:
It is a compile-time error to use the
predefined reference type equality
operators to compare two references
that are known to be different at
compile-time. For example, if the
compile-time types of the operands are
two class types A and B, and if
neither A nor B derives from the
other, then it would be impossible for
the two operands to reference the same
object. Thus, the operation is
considered a compile-time error.
I would imagine that you need to actually override the == operator for the types you are interested in. Whether the compile/runtime will still complain even if the types are implicity convertable is something you'll have to experiment with.
public static bool operator ==(a a, b b)
{
//Need this check or we can't do obj == null in our Equals implementation
if (((Object)a) == null)
{
return false;
}
else
{
return a.Equals(b);
}
}
Alternatively just use Equals implementations like ole6ka suggests and ensure that the implementation does the type casting you need.
http://msdn.microsoft.com/en-us/library/8edha89s.aspx
In each case, one parameter must be
the same type as the class or struct
that declares the operator (...)
Use this
bool c = a.Equals(b);

Nested implicit operator

I have a custom class named Optional< T >. This class overrides the implicit operator, something like this:
public static implicit operator Optional<T>(T value)
{
return new Optional<T>(value);
}
public static implicit operator T(Optional<T> value)
{
return value.value;
}
Ok, till now all fine, the direct operations like these work:
Optional<int> val = 33; //ok
int iVal = val; //ok
But, this doesn't work:
Optional<Optional<int>> val = 33; //error, cannot convert implicitly
So I'm wondering how can be supported the previous case.
Cheers.
You cannot chain user-defined implicit operators. If you define an implicit conversion from A to B, and from B to C, there is no implicit conversion from A to C that calls both. You'll either need to create a separate user-defined conversion from A to C, or have an explicit conversion to do half of the conversion for you.

Why is Explicit Operator Not Invoked In Generic Method

I've distilled this question down to the simplest code sample I can think of. Why is the explicit operator not invoked from the generic method?
class Program
{
static void Main(string[] args)
{
A a = new A();
B b = (B)a; // works as expected
b = Cast<B>(a);
}
static TResult Cast<TResult>(object o)
{
return (TResult)o; // throws Invalid Cast Exception
}
}
class A
{
}
class B
{
public static explicit operator B(A a)
{
return new B();
}
}
Because a generic method has one set of IL, based around TResult. It doesn't do different IL per-caller. And the generic TResult does not have any operators.
Also: the operator here would need to be between object and TResult, and you can't define operators involving object.
For example: Cast<int> and Cast<string> and Cast<Guid> all have the exact same IL.
You can cheat with dynamic:
return (TResult)(dynamic)o;
What it comes down to is that the implicit and explicit operators aren't true conversion operators; they're entirely compile time syntactic sugar. Once the code is compiled nothing about the conversion operators remains.
When the compiler sees:
B b = (B) new A();
It says, "is there any native conversion (implicit or explicit) from an A to a B?" (This would be the case if A extended B, for example), or for one of the few special cased language conversions such as double to int (implicit) or int to double (explicit).)
If not, it then looks for user defined conversion operators (it only looks in the definition of A and B, it doesn't look in the definition of C for an implicit conversion from A and to B, for example). If it finds one, then it injects that operator as a static method call, so the code ends up looking like:
B b = B.SomeAutogneratedName(new A());
That way by the time you get to runtime it's just executing another method, something the runtime knows how to do. The only actual runtime conversion operators that are allowed are the handful baked into the language (i.e. from any base type to a parent type, and between certain primitive types).

c++ cast overload equivalent

I wonder if there is a possibility in c++ to achieve the same cast overloading like in this C# example:
class A {
public static implicit operator A(string s) {
return new A();
}
public static implicit operator A(double d) {
return new A();
}
static void Main(string[] args) {
A a = "hello";
A b = 5.0;
}
}
In C++ it should be something like this:
#include <string>
using namespace std;
class A{
/*SOME CAST OVERLOADING GOES HERE*/
};
void main(){
A a = "hello";
A b = 5.0;
}
Can you help me how I can make this cast overloading?
This is typically achieved with constructors:
class A
{
public:
A(const std::string & s) { /*...*/ }
A(double d) { /*...*/ }
//...
};
Usage: A a("hello"), b(4.2), c = 3.5, d = std::string("world");
(If you declare the constructor explicit, then only the first form ("direct initialization", with the parentheses) is allowed. Otherwise the two forms are entirely identical.)
A one-parameter constructor is also called a "conversion constructor" for this very reason: You can use it to construct an object from some other object, i.e. "convert" a double, say, to an A.
Implicit conversion is widely used. For example, it'll apply in the following situation:
void f(A, const A&) { /* ... */ }
int main() { f(1.5, std::string("hello")); }
This constructs temporaries A(1.5) and A("hello") and passes them to f. (Temporaries also bind to constant references, as you can see with the second argument.)
Update: (Credits to #cHao for looking this up.) According to the standard (12.3.4), "At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value." (This refers to implicit conversion; direct-initialization a la A a("hello"); doesn't fall under this*. See this related question.) So unfortunately you cannot say f("hello", "world");. (Of course you can add a const char * constructor, which in the new C++11 you can even forward to the string constructor with almost no effort.)
*) I think this is actually a bit more subtle. Only user-defined conversions are affected by the rule. So first off, you get a fundamental conversion from char (&)[6] to const char * for free. Then you get one implicit conversion from const char * to std::string. Finally, you have an explicit conversion-construction from std::string to A.
A conversion operator will provide an implicit conversion from your type to some other type. For example:
class A
{
public:
operator std::string () const { return "foo"; }
};
However, implicit conversions can be dangerous because it can be employed when you don't expect it to be employed and would prefer a compiler error. Therefore, it's often better to implement a convert constructor on the destination object, rather than a conversion operator on the source object.
class A
{
public:
A(const std::string& rhs) { /* ... */ }
};
There is still some implicit conversion possible with this, however. Consider:
string f = "foo";
A foo = f;
f is implicitly converted to an A because the convert constructor is available. It might be best for this to result in a compiler error, however. By marking the convert constructor explicit, you make it so that you can easily convert from one type to another, but only when you really intended to.
#include <string>
using namespace std;
class A
{
public:
A() {};
explicit A(const std::string& ) {};
};
int main()
{
string f = "foof";
A a1 = f; // WONT COMPILE
A a(f); // WILL COMPILE
}
There's no need for casting ... this can easily be done by creating constructors for you class that take the correct argument-type, and only use a single argument so they can be called implicitly by the compiler.
If your constructors take more than one non-default argument, then they can't be used in conversion operations.
Furthermore, if you ever want to avoid ambiguity in the selection of constructors based on inferred type conversion, you can always use the explicit keyword with your constructors. For instance, imagine a scenario like:
struct test
{
int a;
test (signed int b): a(b) {}
test (unsigned int b): a(b) {}
};
int main()
{
test A = 5.0;
return 0;
}
This will result in a compiler error due to ambiguity in the conversion of the double type to a scaler-type ... the double type could be implicitly converted to both types. This can be fixed through the use of the explicit keyword to-do the following:
struct test
{
int a;
test (signed int b): a(b) {}
explicit test (unsigned int b): a(b) {}
};
Now the unsigned int version of the constructor for test will only be called if it's passed an unsigned int. A conversion operation from a double type will use the default int version, removing the ambiguity problem.
What it means in C# ?
In C#, the semantics is not about overloading the operator = (which you can't), but provide a cast operator from any type to the type A, or from the type A to any type.
This means the following code (completed with the A-to-type conversions):
class A {
public static implicit operator A(string s) {
return new A();
}
public static implicit operator A(double d) {
return new A();
}
public static implicit operator string(A a) {
return string.Empty;
}
public static implicit operator double(A a) {
return 0.0;
}
static void Main(string[] args) {
A a = "hello"; // line A
A b = 5.0; // line B
a = "World"; // line C
a = 3.14; // line D
double d = a ; // line E
string s = a ; // line F
}
}
works for assignment, either in simple assignments (like lines C and D) or assignment in declarations (like lines A and B). The line E and F demonstrate how we can convert from the user type to other types.
How to do it in C++ ?
In C++, the lines A and B are object constructions, which will call the relevant constructors.
The lines C and D are assignment, which will call the relevant assignment operator.
So the C++ code for the C# code you provided must provide both constructors and assignments for string and doubles, as follows:
class A
{
public:
A(const std::string & s) { /*...*/ }
A(double d) { /*...*/ }
A & operator = (const std::string & s) { /*...*/ ; return *this ; }
A & operator = (double d) { /*...*/ ; return *this ; }
// etc.
} ;
This way, you can have
void main()
{
A a0 = "Hello" ; // constructor
A a1("Hello") ; // constructor
A a2 = 5.0 ; // constructor
A a3(5.0) ; // constructor
a0 = "Hello World" ; // assignment
a0 = 3.14 ; // assignment
}
What about cast operators in C++ ?
The cast operators in C++ work like the following C# converter operators:
class A
{
static public operator string(A a) { /*... ; return a string */ }
static public operator double(A a) { /*... ; return a double */ }
// etc.
}
In C++, the cast operators are written as such:
class A
{
public:
operator std::string() { /*... ; return a string */ }
operator double() { /*... ; return a double */ }
// etc.
} ;
void main()
{
A a ;
std::string s ;
double d ;
// etc.
s = a ; // cast operator
d = a ; // cast operator
}
Why is it so complicated in C++ ?
In C#, a cast/conversion operator can be written from the class type to any type, or from any type to the class type.
In C++, for conversion, you must choose between one or more ways, that is: constructor, assignment operator or cast operator, because in C++, you have a fine grained control on types and operation, and thus, you must use that fine grained API.
Constructions means the object does not exist, so there is no point in constructing it to some default value and then assigning it another value (this can cost in speed). In C#, the reference/value type is null/zeroed before the assignment, so this is not an issue.
Assignment means the object does already exist, so it is possible one can reuse the same internals to accommodate the new value. In C#, the original reference object is discarded, and another is created (if the creation costs, that you'll pay that price)
As for the cast, it is used in the current case to convert an existing class into another for which you have no control: You have no right to extend the std::string to accommodate a constructor for your class, and there is no way to add a constructor to a built-in type like double.
You can provide a constructor with one argument of your desired type:
class Foo {
int bar;
public:
Foo(const int bar) : bar(bar) {};
}
Foo foo = 5;

Casting Between Data Types in C#

I have (for example) an object of type A that I want to be able to cast to type B (similar to how you can cast an int to a float)
Data types A and B are my own.
Is it possible to define the rules by which this casting occurs?
Example
int a = 1;
float b = (float)a;
int c = (int)b;
Yes, this is possible using C# operator overloading. There are two versions explicit and implicit.
Here is a full example:
class Program
{
static void Main(string[] args)
{
A a1 = new A(1);
B b1 = a1;
B b2 = new B(1.1);
A a2 = (A)b2;
}
}
class A
{
public int Foo;
public A(int foo)
{
this.Foo = foo;
}
public static implicit operator B(A a)
{
return new B(a.Foo);
}
}
class B
{
public double Bar;
public B(double bar)
{
this.Bar = bar;
}
public static explicit operator A(B b)
{
return new A((int)b.Bar);
}
}
Type A can be cast implicitly to type B but type B must be cast explicitly to type A.
Assuming you want that to be an explcit operation you'll need to write an explicit cast operator like so:
public static explicit operator MyTypeOne(MyTypeTwo i)
{
// code to convert from MyTypeTwo to MyTypeOne
}
You can then use it like so:
MyTypeOne a = new MyTypeOne();
MyTypeTwo b = (MyTypeTwo)a;
I'd question whether you want to actually cast one type to another, or whether you actually want to convert instead. I'd say you should avoid writing cast operators for conversions, if you are just aiming to take advantage of a nice syntax :)
Also, in general it is advised not to use implicit casts, as they allow for unintended type converstions. From MSDN documentation on implicit:
However, because implicit conversions
can occur without the programmer's
specifying them, care must be taken to
prevent unpleasant surprises. In
general, implicit conversion operators
should never throw exceptions and
never lose information so that they
can be used safely without the
programmer's awareness.
You cant overload the cast operator in c# but you can use explicit and implicit conversion operators instead:
"Using Conversion Operators (C# Programming Guide)"

Categories

Resources