472,805 Members | 1,612 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,805 software developers and data experts.

Operator inheritance compiler error ?

I am getting a compiler error that I can't well explain or even
understand the origin of (though I boiled it down close...). Below is
a bare-bones example.

What I am doing is defining the increment operator in a class and then
defining a derived class and using the derived class. When I try to
use the increment operator on an instance of the derived class (as an
instance of the derived class, not when used as an instance of the base
class), I get a compiler error (detailed below). While I realize that
there are a number of options I can use to get around this, the problem
itself has become a point of interest.

This code causes the compiler error:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;

namespace QuickTest {
public class Bar : ReadOnlyCollection<string{
protected int _CurrentPosition; //The currently active
value.
protected Bar _NextMostSignificantBar; //The next most
significant value (as in 2 is the next significant digit from the
perspective of 7 in the number 27).

public Bar( Bar bar )
: base( new List<string>() ) {
_CurrentPosition = 0;
_NextMostSignificantBar = bar;
}

public static Bar operator ++( Bar bar ) {
if ( ( bar._CurrentPosition + 1 ) == bar.Count ) {
if ( null != bar._NextMostSignificantBar ) {
bar._NextMostSignificantBar++;
}
bar._CurrentPosition = 0;
}
else { bar._CurrentPosition++; }
return bar;
}

public static implicit operator string( Bar bar ) {
return bar[bar._CurrentPosition];
}
}

public class NumericBar : Bar {
public NumericBar( Bar bar )
: base( bar ) {
this.Items.Add( "0" );
this.Items.Add( "1" );
this.Items.Add( "2" );
this.Items.Add( "3" );
this.Items.Add( "4" );
this.Items.Add( "5" );
this.Items.Add( "6" );
this.Items.Add( "7" );
this.Items.Add( "8" );
this.Items.Add( "9" );
}
}

public class Program {
public static void Main( string[] args ) {
NumericBar tens = new NumericBar( null );
NumericBar ones = new NumericBar( tens );
for ( int i = 0; i < tens.Count * ones.Count + 2; i++ ) {
Console.WriteLine( tens + ones );
// Causes compiler error: error CS0266: Cannot
implicitly
// convert type 'QuickTest.Bar' to
'QuickTest.NumericBar'.
// An explicit conversion exists (are you missing a
cast?)
ones++;
}
Console.WriteLine( "Press any key to exit." );
Console.ReadKey();
}
}
}

On the other hand, if I change the increment operator definition to
read (define it as a function, rather than an operator):

public static Bar op_Increment( Bar bar ) {
if ( ( bar._CurrentPosition + 1 ) == bar.Count ) {
if ( null != bar._NextMostSignificantBar ) {
Bar.op_Increment( bar._NextMostSignificantBar );
}
bar._CurrentPosition = 0;
}
else { bar._CurrentPosition++; }
return bar;
}

And the consuming code to:
Bar.op_Increment( ones );
I don't have any problems, but it is kind of nasty and really defeats
the whole idea (Besides, at this point, I was wondering what was going
wrong).

If I leave the increment operator definition as is and change the
consuming code to read:
((Bar)ones)++;

I get the following run-time exception:
System.InvalidProgramException was unhandled
Message="Common Language Runtime detected an invalid program."
Source="QuickTest"
StackTrace:
at QuickTest.Program.Main(String[] args)
at System.AppDomain.nExecuteAssembly(Assembly assembly, String[]
args)
at System.AppDomain.ExecuteAssembly(String assemblyFile,
Evidence assemblySecurity, String[] args)
at
Microsoft.VisualStudio.HostingProcess.HostProc.Run UsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context( Object
state)
at System.Threading.ExecutionContext.Run(ExecutionCon text
executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

Which doesn't make a whole lot of sense at first, but I believe that
the attempted cast is causing the NumericBar( Bar bar ) constructor to
be used (when it attempts to cast the object from the Bar class to a
NumericBar object) and thereby calling the constructor without using
new and passing it the object that is to be cast as it's
"parent"...

The problem can be circumvented if I change the line:
NumericBar ones = new NumericBar( tens );
To read:
Bar ones = new NumericBar( tens );
But that is a confusing requirement to place on an end developer and
I'm not sure how or where we would document it.

Since the conceptual paradigm of the code seems not to make sense, the
next step I took was to look into the IL code that expresses the
operator definition. It turns out that the only difference is the
translation of the "recursive" call on the "next most significant
Bar" (when rolling around) within the operator/function definition.
Commenting out that line of code (the "recursive" call) does not
make the compiler error go away, so here is the IL code that is
produced:

//000022: Bar.op_Increment(
bar._NextMostSignificantBar );
IL_0027: /* 02 | */ ldarg.0
IL_0028: /* 7B | (04)000002 */ ldfld class
QuickTest.Bar/*02000002*/
QuickTest.Bar/*02000002*/::_NextMostSignificantBar /* 04000002 */
IL_002d: /* 28 | (06)000002 */ call class
QuickTest.Bar/*02000002*/ QuickTest.Bar/*02000002*/::op_Increment(class
QuickTest.Bar/*02000002*/) /* 06000002 */
IL_0032: /* 26 | */ pop
.line 23,23 : 17,18 ''

Rather than:
//000020: bar._NextMostSignificantBar++;
IL_0027: /* 02 | */ ldarg.0
IL_0028: /* 25 | */ dup
IL_0029: /* 7B | (04)000002 */ ldfld class
QuickTest.Bar/*02000002*/
QuickTest.Bar/*02000002*/::_NextMostSignificantBar /* 04000002 */
IL_002e: /* 28 | (06)000002 */ call class
QuickTest.Bar/*02000002*/ QuickTest.Bar/*02000002*/::op_Increment(class
QuickTest.Bar/*02000002*/) /* 06000002 */
IL_0033: /* 7D | (04)000002 */ stfld class
QuickTest.Bar/*02000002*/
QuickTest.Bar/*02000002*/::_NextMostSignificantBar /* 04000002 */
.line 21,21 : 17,18 ''

The main difference is the dup, ldfld, call, and then use of stfld
rather than a ldfld, call, and then pop in the sequence of calls that
represents the . Those four calls are documented as (a fifth is listed
and will be implicitly referred to later):

Nerfed Table of the opcodes:
Format
Assembly Format
Description

7D < T >
stfld field
Replaces the value of field of the object with a new value.

7B < T >
ldfld field
Pushes the value of a field in a specified object onto the stack.

28 < T >
call methodDesc
Call the method described by methodDesc.

25
dup
Duplicates the value on the top of the stack.

FE 0E < unsigned int16 >
stloc index
Pops a value from the stack and stores it in local variable index.
So... the increment operator case duplicates the
_NextMostSignificantBar field, then executes the increment call on it
and tries to reassign the duplicated _NextMostSignificantBar to the
object's _NextMostSignificantBar.

Then I found that trying to comment out the "recursive" increment
operation wasn't targeting the error, since the error is occurring in
the code in Main(str[]), so I moved on to looking at that code (using
the NumericBar =Bar definition hack to produce the code). Here is
the IL:

//000060: ones++;
IL_002d: /* 07 | */ ldloc.1
IL_002e: /* 28 | (06)000002 */ call class
QuickTest.Bar/*02000002*/ QuickTest.Bar/*02000002*/::op_Increment(class
QuickTest.Bar/*02000002*/) /* 06000002 */
IL_0033: /* 0B | */ stloc.1
And in the case of the function call:
//000061: Bar.op_Increment( ones );
IL_002d: /* 07 | */ ldloc.1
IL_002e: /* 28 | (06)000002 */ call class
QuickTest.Bar/*02000002*/ QuickTest.Bar/*02000002*/::op_Increment(class
QuickTest.Bar/*02000002*/) /* 06000002 */
IL_0033: /* 26 | */ pop

Of course, the difference is the use of stloc.1 (which copies the value
on the stack to the "ones" object [which is itself, but had been
implicitly cast to a Bar object]) rather than pop, which is sufficient
since the changed data is referenced within the "ones" object. The
stloc's copy action seems (I'm guessing) to be type-safe (or maybe
the compiler/VS just analyzes it as a type cast) and is thereby the
cause of the compiler error. Remember that the increment operator
takes a Bar object and returns a Bar object (the same object passed in)
and that the increment is being called on a NumericBar object.

With the problem identified, I am at a point where all I can do is ask
a question: what is the correct way to define an increment operator
that will be usable on the derived classes of the defining superclass?
Is this even possible? It seems sensible. Is this a bug (or simply a
unexpected use) in the operator/class structure relationship?

I don't need a solution really, what I have done is to define a
non-static virtual method "Increment" that is called from the short
definition of the ++ operator that I have defined on all the derived
classes:

public static Bar operator ++( Bar bar ) {
bar.Increment();
return bar;
}

I did find a discussion of the inheritance of overloaded operators at:
http://www.codeproject.com/csharp/cssimpolymorph.asp where Jeffery Sax
explained that inheritance of operator overloading can cause ambiguity,
but that was based on ordering effects with binary operators and in the
case of a unary operator (which must be defined statically targeting
the specific defining type), ordering is not an issue.

Nov 22 '06 #1
0 1977

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

8
by: Clifton M. Bean | last post by:
First, I defined three classes (listed below): =========== // 1st class =========== class PointCl { public: PointCl & operator= (const PointCl & rgh ); //define as usual assingment operator
2
by: RR | last post by:
I'm sure this has been answered before but two hours of searching and reading hasn't answered this question for me. Here's a test program: ******************************** class base {...
20
by: Patrick Guio | last post by:
Dear all, I have some problem with insertion operator together with namespace. I have a header file foo.h containing declaration of classes, typedefs and insertion operators for the typedefs in...
3
by: danilo.horta | last post by:
Hi folks I'm having a scope resolution issue. The gnu compiler is trying to use the "operator function" from derived class rather than from correct one, the base class. // VecBasis.h file...
2
by: Joachim | last post by:
Hello, I have been trying to get the following code to work, but unfortunately without any succes so far. I have searched in newsgroups and google but found nothing there that could solve my...
13
by: JD | last post by:
Hi, My associate has written a copy constructor for a class. Now I need to add an operator = to the class. Is there a way to do it without change her code (copy constructor) at all? Your help...
17
by: My interest | last post by:
How can I use * operator in C# generics? e.g. class foo<T_> { T_ v1, v2: public T_ squar() { return v1 * v2; /// wrong! }
9
by: wo3kie | last post by:
#include <iostream> #include <map> #include <utility> // // Base // / | \ // Derived1 Derived2 \ // \ | / // Derived3
2
by: Peng Yu | last post by:
Hi, In the following code, the 'copy' member function works. But the '=' operator does not work. Can somebody let me know why a member function is different from an operator. Thanks, Peng ...
2
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 2 August 2023 starting at 18:00 UK time (6PM UTC+1) and finishing at about 19:15 (7.15PM) The start time is equivalent to 19:00 (7PM) in Central...
0
linyimin
by: linyimin | last post by:
Spring Startup Analyzer generates an interactive Spring application startup report that lets you understand what contributes to the application startup time and helps to optimize it. Support for...
0
by: erikbower65 | last post by:
Here's a concise step-by-step guide for manually installing IntelliJ IDEA: 1. Download: Visit the official JetBrains website and download the IntelliJ IDEA Community or Ultimate edition based on...
0
by: kcodez | last post by:
As a H5 game development enthusiast, I recently wrote a very interesting little game - Toy Claw ((http://claw.kjeek.com/))。Here I will summarize and share the development experience here, and hope it...
14
DJRhino1175
by: DJRhino1175 | last post by:
When I run this code I get an error, its Run-time error# 424 Object required...This is my first attempt at doing something like this. I test the entire code and it worked until I added this - If...
0
by: Rina0 | last post by:
I am looking for a Python code to find the longest common subsequence of two strings. I found this blog post that describes the length of longest common subsequence problem and provides a solution in...
5
by: DJRhino | last post by:
Private Sub CboDrawingID_BeforeUpdate(Cancel As Integer) If = 310029923 Or 310030138 Or 310030152 Or 310030346 Or 310030348 Or _ 310030356 Or 310030359 Or 310030362 Or...
0
by: Mushico | last post by:
How to calculate date of retirement from date of birth
2
by: DJRhino | last post by:
Was curious if anyone else was having this same issue or not.... I was just Up/Down graded to windows 11 and now my access combo boxes are not acting right. With win 10 I could start typing...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.