473,416 Members | 1,564 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,416 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 2023

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 ...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...

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.