473,756 Members | 1,823 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

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.Collecti ons.Generic;
using System.Collecti ons.ObjectModel ;
using System.Text;

namespace QuickTest {
public class Bar : ReadOnlyCollect ion<string{
protected int _CurrentPositio n; //The currently active
value.
protected Bar _NextMostSignif icantBar; //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>() ) {
_CurrentPositio n = 0;
_NextMostSignif icantBar = bar;
}

public static Bar operator ++( Bar bar ) {
if ( ( bar._CurrentPos ition + 1 ) == bar.Count ) {
if ( null != bar._NextMostSi gnificantBar ) {
bar._NextMostSi gnificantBar++;
}
bar._CurrentPos ition = 0;
}
else { bar._CurrentPos ition++; }
return bar;
}

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

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.WriteLi ne( tens + ones );
// Causes compiler error: error CS0266: Cannot
implicitly
// convert type 'QuickTest.Bar' to
'QuickTest.Nume ricBar'.
// An explicit conversion exists (are you missing a
cast?)
ones++;
}
Console.WriteLi ne( "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._CurrentPos ition + 1 ) == bar.Count ) {
if ( null != bar._NextMostSi gnificantBar ) {
Bar.op_Incremen t( bar._NextMostSi gnificantBar );
}
bar._CurrentPos ition = 0;
}
else { bar._CurrentPos ition++; }
return bar;
}

And the consuming code to:
Bar.op_Incremen t( 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.InvalidP rogramException was unhandled
Message="Common Language Runtime detected an invalid program."
Source="QuickTe st"
StackTrace:
at QuickTest.Progr am.Main(String[] args)
at System.AppDomai n.nExecuteAssem bly(Assembly assembly, String[]
args)
at System.AppDomai n.ExecuteAssemb ly(String assemblyFile,
Evidence assemblySecurit y, String[] args)
at
Microsoft.Visua lStudio.Hosting Process.HostPro c.RunUsersAssem bly()
at System.Threadin g.ThreadHelper. ThreadStart_Con text(Object
state)
at System.Threadin g.ExecutionCont ext.Run(Executi onContext
executionContex t, ContextCallback callback, Object state)
at System.Threadin g.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_Incremen t(
bar._NextMostSi gnificantBar );
IL_0027: /* 02 | */ ldarg.0
IL_0028: /* 7B | (04)000002 */ ldfld class
QuickTest.Bar/*02000002*/
QuickTest.Bar/*02000002*/::_NextMostSign ificantBar /* 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._NextMostSi gnificantBar++;
IL_0027: /* 02 | */ ldarg.0
IL_0028: /* 25 | */ dup
IL_0029: /* 7B | (04)000002 */ ldfld class
QuickTest.Bar/*02000002*/
QuickTest.Bar/*02000002*/::_NextMostSign ificantBar /* 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*/::_NextMostSign ificantBar /* 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
_NextMostSignif icantBar field, then executes the increment call on it
and tries to reassign the duplicated _NextMostSignif icantBar to the
object's _NextMostSignif icantBar.

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_Incremen t( 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 2056

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

Similar topics

8
4164
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
1803
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 { public: }; class der : protected base
20
3871
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 a named namespace namespace foo { class Foo
3
2091
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 template<class T, size_t numDim> class VecBasis { protected:
2
1528
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 problem. Given the following classes : template <class T> class AAA{ int tabel; public:
13
3970
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 is much appreciated. JD
17
1902
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
2063
by: wo3kie | last post by:
#include <iostream> #include <map> #include <utility> // // Base // / | \ // Derived1 Derived2 \ // \ | / // Derived3
2
1788
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 #include <iostream>
0
9275
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 effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10040
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
9873
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 tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
9846
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 Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
9713
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 protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
8713
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
5304
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3806
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
3
2666
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

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.