• Advertisement
Sign in to follow this  

Error overloading '-' Operator

This topic is 4038 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

error C2808: unary 'operator ->' has too many formal parameters ...is an error I received after attempting to overload said operator. While it was easy enough to change which operator I was overloading, I'm still confused as to why this one, out of all possible operators, is a black sheep. If for nothing more than sheer curiosity, can anyone explain that to me?

Share this post


Link to post
Share on other sites
Advertisement
Can you post the code? The error description makes it pretty easy to guess the cause of the problem, but we'll be able to give you more specific feedback if we can see what you're actually trying to do.

Share this post


Link to post
Share on other sites
class Base_Stat {
int Base_Score, Base_Mod, Temp_Score, Temp_Mod, Misc_Mod;
string Stat_Name;
Base_Stat ();
Base_Stat (string);
Base_Stat operator -> (Base_Stat);
int Get_Mod(int);
void Set_Base (int);
void Change_Base (int);
void Change_Temp (int);
void Print_Stats ();
friend class Character;
};



...is the class in which I declare the overload...

Base_Stat Base_Stat::operator -> (Base_Stat param) {
param.Stat_Name = Stat_Name;
return param;
}



...and there is it's definition. After compiling with those, I got the previous error, but after switching the -> with a <=, the program compiled and ran nicely. I'm now having a bit of trouble using this overloaded operator, but it doesn't seem to be something I can't handle (although any advice would be duly appreciated).

As for what I'm attempting to do, I've made a class that has several other classes as members (Base_Stat being the one you've seen). When declaring the class, a default constructor is used to add Base_Stat to the large class, but I've had some problems initializing a non-default constructor while declaring another class. So I'm attempting to side-step the problem and simply give myself a way to modify the default-created Base_Stat with a properly constructed one, by way of overloading an operator. This error simply maybe me raise an eyebrow.

Share this post


Link to post
Share on other sites
13.5.6 - "operator-> shall be a non-static member function taking no parameters."

Share this post


Link to post
Share on other sites
Quote:
Original post by Washu
13.5.6 - "operator-> shall be a non-static member function taking no parameters."


...which to me says "no overloading." I'll keep that in mind, thank you for the response!

Share this post


Link to post
Share on other sites
It doesn't mean you can't overload it.

You should understand how operator-> works, though. When you call operator-> on an object, it keeps getting applied to the return value of operator-> until it is applied to a pointer type, then it's used to access one of the members of that object.

Like this:


struct A
{
void foo() const
{
std::cout << "A::Foo() called\n";
}
};

struct B
{
A x;
A const* operator->() const
{
std::cout << "B::operator->() called\n";
return &x;
}
};

struct C
{
B x;
B const& operator->() const
{
std::cout << "C::operator->() called\n";
return x;
}

};

struct D
{
C x;
C const& operator->() const
{
std::cout << "D::operator->() called\n";
return x;
}

};

int main()
{
D d;
d->foo();
}



Will print:


D::operator->() called
C::operator->() called
B::operator->() called
A::Foo() called

Share this post


Link to post
Share on other sites
I understand how it works in normal circumstances, but I thought overloading simply made the operator have completely different functionality, which you define yourself, meaning that it wouldn't matter how the -> works as a pointer function; I told it to do something else entirely when I overloaded it as a class operator.

Is this incorrect then?

Share this post


Link to post
Share on other sites
Quote:
Original post by hereticprophecy
I understand how it works in normal circumstances, but I thought overloading simply made the operator have completely different functionality, which you define yourself, meaning that it wouldn't matter how the -> works as a pointer function; I told it to do something else entirely when I overloaded it as a class operator.

Is this incorrect then?


You can't go wild and invent new valid syntax. For instance, how did you plan on passing a parameter to your overloaded operator->? obj->(arg) ??

operator-> is a unary function, you can't make it binary, the syntax doesn't support it.

Share this post


Link to post
Share on other sites
If I knew the difference between 'unary' and 'binary' operators, I wouldn't be posting this. Unary = 'one-directional'?

I'm toying with syntax, as I'm teaching myself the language through various sources, and when I saw in the cplusplus.com tutorial that the + operator could be modified to have a vastly different function than it normally has, I assumed the others listed there could do the same. Passing parameters with those overloaded functions was easy, and I'd made it work in several test programs; I simply tried something new and was made curious by the result.

Share this post


Link to post
Share on other sites
Quote:
Original post by RDragon1
Quote:
Original post by hereticprophecy
I understand how it works in normal circumstances, but I thought overloading simply made the operator have completely different functionality, which you define yourself, meaning that it wouldn't matter how the -> works as a pointer function; I told it to do something else entirely when I overloaded it as a class operator.

Is this incorrect then?


You can't go wild and invent new valid syntax. For instance, how did you plan on passing a parameter to your overloaded operator->? obj->(arg) ??

operator-> is a unary function, you can't make it binary, the syntax doesn't support it.


its also a postfix expression, as per 5.2.5. Its a combination of a postfix expression (p1), followed by -> followed by an id-expression. The postfix expression p1 is evaluated, the result of that evaluation together with the id-expression determine the result of the entire postfix expression.

Because of that, its evaluated as a unary operator, but the clarification should be present. Especially since its behavior is fairly specialized.

Share this post


Link to post
Share on other sites
Quote:
Original post by hereticprophecy
If I knew the difference between 'unary' and 'binary' operators, I wouldn't be posting this. Unary = 'one-directional'?

I'm toying with syntax, as I'm teaching myself the language through various sources, and when I saw in the cplusplus.com tutorial that the + operator could be modified to have a vastly different function than it normally has, I assumed the others listed there could do the same. Passing parameters with those overloaded functions was easy, and I'd made it work in several test programs; I simply tried something new and was made curious by the result.


A unary operator has only one operand. Unary operator-, the address-of operator&, operator!, operator->, etc are examples of unary operators.

A binary operator has two arguments. Most/all of the arithmetic operators have two operands.

Share this post


Link to post
Share on other sites
Quote:
A unary operator has only one operand. Unary operator-, the address-of operator&, operator!, operator->, etc are examples of unary operators.

A binary operator has two arguments. Most/all of the arithmetic operators have two operands.


Quote:
its also a postfix expression, as per 5.2.5. Its a combination of a postfix expression (p1), followed by -> followed by an id-expression. The postfix expression p1 is evaluated, the result of that evaluation together with the id-expression determine the result of the entire postfix expression.

Because of that, its evaluated as a unary operator, but the clarification should be present. Especially since its behavior is fairly specialized.


I appreciate the clarification, thank you.

++ and -- are also examples of unary operators, then?

Share this post


Link to post
Share on other sites
Quote:
Original post by hereticprophecy
I appreciate the clarification, thank you.

++ and -- are also examples of unary operators, then?


Yep, they are. There's a little caveat involved with those operators, as there's two forms of unary operator++ - one is preincrement, the other postincrement. Same for operator--

Share this post


Link to post
Share on other sites
Even if it were allowed, it would be extremely bad form to invent your own meaning for such a specialized operator. operator-> has a specific meaning, that is, indirectly accessing a member, and any other semantics would be extremely unintuitive and surprising. Obviously, the same applies to other overloadable operators, too; you wouldn't use operator+ to implement a swap, would you?

Share this post


Link to post
Share on other sites
The arrow signified to me a passing of information, which was exactly what I had intended it to do. I'm now using <= to do the same thing (passing a member from the right to the left), and since it's use is so limited when overloaded, I couldn't imagine a situation where it would get confusing.

That does lead me to another question, though: do overloaded operators work like overloaded constructors, in that the default is used unless the specified parameter is passed? I realize this doesn't apply to unary operators, but does overloading <= make it impossible for me to check if something is less-than-or-equal to something else in this program, even if I'm working with simple numbers and not classes?

Share this post


Link to post
Share on other sites
Quote:
Original post by hereticprophecy
That does lead me to another question, though: do overloaded operators work like overloaded constructors, in that the default is used unless the specified parameter is passed? I realize this doesn't apply to unary operators, but does overloading <= make it impossible for me to check if something is less-than-or-equal to something else in this program, even if I'm working with simple numbers and not classes?

It's not so much like that. You haven't defined the operator for anything but your class, so it's the only thing that'll pay any attention to it. To use your constructor analogy, why would a 3DVector constructor be used when trying to make a MagicalElfWithSwordOfMutilation object? Sure, there's 3DVector constructor that has three float parameters ((float fPower, float fHealth, float fAwesomeness), right?), but the MEWSOM object won't use it. It will use its own tri-float constructor, or, failing that, the default.

So, you can still use that operand everywhere else you'd want to. You just can't check two of your class instances for less-than-or-equality with it.

I think that clears that up! [smile]
-jouley

Share this post


Link to post
Share on other sites
Quote:
Original post by hereticprophecy
The arrow signified to me a passing of information, which was exactly what I had intended it to do. I'm now using <= to do the same thing (passing a member from the right to the left), and since it's use is so limited when overloaded, I couldn't imagine a situation where it would get confusing.

That does lead me to another question, though: do overloaded operators work like overloaded constructors, in that the default is used unless the specified parameter is passed? I realize this doesn't apply to unary operators, but does overloading <= make it impossible for me to check if something is less-than-or-equal to something else in this program, even if I'm working with simple numbers and not classes?


Generally for passing information in, we overload << and for passing out we overload >>, as per the standard library streams.

Not only does this have the correct precidence that allows for chaining, it is also far less likely you need to perform bit-shifting on a class (i.e. the built in meaning of << and >>) than doing less-than-or-equal-to.


class x
{
public:
x &operator<<(int i){ /* whatever */ return *this; }
};

void f()
{
x v;

v << 1 << 2 << 3; // and so on
}

Share this post


Link to post
Share on other sites
Quote:
Generally for passing information in, we overload << and for passing out we overload >>, as per the standard library streams.

Not only does this have the correct precidence that allows for chaining, it is also far less likely you need to perform bit-shifting on a class (i.e. the built in meaning of << and >>) than doing less-than-or-equal-to.


Makes sense, I'll get in that habit then.

Share this post


Link to post
Share on other sites
Quote:
Original post by hereticprophecy
The arrow signified to me a passing of information


Which information? Why? The name, apparently, but

(a) Why would you want to set the name of one stat to the name of another, without changing any of the other information?

(b) How do you expect to know, looking at the arrow symbol, which information is "passed"?

In general, operators should do something that resembles their "normal" meaning. Yes, the stream objects do sort of break that rule with operator<< and operator>>, because those have different meanings for numbers. Cases where this is accepted are fewer and further between, but generally they at least follow the next rule of not potentially being confused with something else. "Obviously" the operator<< of std::cout isn't a bit-shift, because std::cout "obviously" isn't a numerical type, so there is no confusion. But just about anything could "pretend" to be a pointer somewhat sanely - this is the concept of a "handle" - so operator->, in general, has very specific expectations.

Quote:

, which was exactly what I had intended it to do. I'm now using <= to do the same thing (passing a member from the right to the left), and since it's use is so limited when overloaded, I couldn't imagine a situation where it would get confusing.


Similarly, comparison operators really, really only should be used for comparing things, because it's hard to define something that's "obviously not able to be compared to other things".

Consider that relational operators often get implemented in terms of each other, too. In your case, you probably will get away with it, but the language has a habit in particular of using a class' operator== and operator< to synthesize the others.




Anyway. If you were just showing that assignment as an example, and you actually want to "pass" *all* the data members, then there's a very natural and obvious choice for the operator to use: operator= .




Now, here's the *advanced* lecture, for the day when you *do* have a good reason to overload this operator. ;) As it happens, overloading operator-> doesn't work exactly as you'd expect it to. It *looks* binary, after all - there's something relevant on either side of the symbol. But the RHS isn't a variable; it's a member-name of a structure. What operator-> is actually *expected* to do is - hold on to your hat - return either a pointer to something (which will then, in turn, use the "native" -> logic - i.e. the pointer is dereferenced and the member selected from the pointed-to thing), or else return some non-pointer type, in which case the returned thing gets *its* operator-> called... this may continue in a chain until either (a) a pointer type is returned, in which case the pointer is used with -> "normally", or (b) the returned thing is a non-pointer and also doesn't have an operator overload, in which case the compiler reports an error.

In practice, you almost always write operator-> to return a pointer. Usually, operator-> comes in pairs with an overload of the unary operator* (that is to say, the dereference operator; multiplication is a binary operator*, which is how the language knows the difference). Given a working operator* that does what it ought to, in that case, you could implement operator-> like this (now you REALLY need to hang on to your hat):


// We have a class Bar which is a "handle" for Foos. That is, it provides a
// pointer-to-Foo interface. BTW, standard library iterators work rather like
// this as well.
Foo& Bar::operator*() {
// Dereferencing a Bar yields the Foo to which this Bar "points".
// We return the pointed-at thing by reference - important! We don't
// return a pointer because we are dereferencing, and we don't return a
// value because that would *copy* the Foo, and we want the caller to operate
// on the *same* Foo - so that the caller can change a Foo "via" a Bar.

// Anyway, we don't need to see the logic for this... it might return some
// data member of the Bar class for example, or something else. But let's
// pretend that it's something complicated, so that implementing -> in terms
// of * makes sense.
}

// Now, the corresponding operator->. Remember, we return a pointer:
Foo* Bar::operator->() {
return &**this;
// Got that? Didn't think so. :)
// We need to read the operations from the inside out.
// First, the "this" pointer is dereferenced, to give the current object,
// by reference.
// Then we dereference that again, which - because '*this' is a Bar& and not
// a pointer type - calls our overloaded operator*. That gives us a Foo&.
// Finally, we reference the Foo& to get a Foo& pointing at our Foo.
}

Share this post


Link to post
Share on other sites
Quote:
Which information? Why? The name, apparently, but

(a) Why would you want to set the name of one stat to the name of another, without changing any of the other information?

(b) How do you expect to know, looking at the arrow symbol, which information is "passed"?


Like I said earlier, when declaring my Character class (which has objects of another class as members) it requires a default constructor to compile this mega-class. This default constructor has no way to name these objects correctly, as it would require a constructor with a parameter.

The other objects of this interior class have the rest of the members initialized correctly for each object during the Character's construction process, but as the objects are being initialized (by way of user input) I need a way to let the user know which object they are modifying; in other words, I need the name of each object initialized before the rest of the object is initialized.

I did initially think of using = for this process, but when it came down to it, I didn't want any way for the user to change this object after character creation, and I certainly didn't want my program to do it either. So I limited the transfer of information to just the name (giving the user the opportunity to change the name later, if they wanted, but not the really important info) and chose the operator that made the most sense to me, an arrow/directional change that didn't signify equivalence but instead a simple naming process.

It still may make little sense to an experienced programmer, but I also need practice doing these simple things while I'm still motivated to really practice.

That being said, what in your opinion would be a real reason for overloading an operator? Step on the gas if you need to, my hat is being held ;)...

Share this post


Link to post
Share on other sites
Quote:
Original post by hereticprophecy
That being said, what in your opinion would be a real reason for overloading an operator? Step on the gas if you need to, my hat is being held ;)...


The standard examples of overloading operators are overloading the arithmetic operators for complex or vector types, and overloading reference operators with smart pointers. These are examples of useful operator overloading. In these cases, the meaning of the operator changes very little (if at all).

Changing the meaning of an operator is generally a bad thing because the persons using the operator generally have very strong expectations about how the operator should behave. If the operator doesn't behave as expected, then bugs will result. There are a few ways an overloaded operator can "misbehave":
  • The behavior is similar to the expected behavior, except for a major difference. Consider what would happen if + were overloaded and returned the sum of the two parameters, but also cleared the parameters. What a disaster that would be!
  • The expected precedence of the operator does not match the actual precedence. For example, cout << x & 0xff; doesn't do what you think it does.
  • Overloading arbitrary operators makes understanding the code more difficult (in other words, it reduces readability). For example, what does vector x = a & b; do?
When overloading operators, don't be clever. "Clever" in the programming sense means "unreadable" and "unmaintainable". I think the overloading of << and >> in the standard library was clever, and thus a bad idea (and for two of the reasons listed above).

Share this post


Link to post
Share on other sites
Makes sense.

So I took out the current overload and took a more mainstream approach to my current problem. As I get further into this project, I'll get to another point where overloading an operator *seems* like a good idea.

I'm making a D&D character generator, and my biggest problem with other's I've found is the ability to multiclass...that is, add two or more class templates onto the charater's base stats and situate the proper values in the right places.

If my class Character has an already instantialized object Toon, would "Toon + Fighter" be a readable and unclever solution, assuming Fighter is itself another class?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement