PDA

View Full Version : C++ Overloading - How do I call (int, ...) instead of (int, int, ...)?




ArtOfWarfare
Jun 18, 2012, 11:52 AM
I'm in a class on C++. We've learned about overloading functions, and I decided to mix it with varying numbers of arguments (not a topic we covered in class; something I just know from studying C on my own.)

Anyways, I have a stack class with an overloaded constructor. Here is most of the class:

class Stack {
private:
int *data;
unsigned int max;
unsigned int size;
int top;
public:
Stack(unsigned int, unsigned int, ...);
Stack(unsigned int, ...);
Stack();
~Stack();
void push(int);
int pop();
};

Stack::Stack (unsigned int maximum, unsigned int count, ...)
{
cout << "Stack::Stack (int maximum, int count, ...)" << endl;
if (max < count) max = maximum = count;
data = new int[max];
size = count;
va_list values;
va_start(values, count);
for (int i = 0; i < count; i++)
{
data[i] = va_arg(values, int);
}
va_end(values);
top = data[count-1];
}

Stack::Stack (unsigned int count, ...)
{
cout << "Stack::Stack (int count, ...)" << endl;
max = (MAX_SIZE < count)?count:MAX_SIZE;
data = new int[max];
size = count;
va_list values;
va_start(values, count);
for (int i = 0; i < count; i++)
{
data[i] = va_arg(values, int);
}
va_end(values);
top = data[count-1];
}

Stack::Stack ()
{
cout << "Stack::Stack ()" << endl;
data = new int[MAX_SIZE];
max = MAX_SIZE;
size = top = 0;
}

My issue is that I'd like to call Stack::Stack (int count, ...), but every time I try that, it's calling Stack::Stack (int maximum, int count, ...) instead. Here's the code:

int main (int argc, char* argv[])
{
Stack a (5, 4, 3, 2, 1, 0);
cout << a.pop() << a.pop() << a.pop() << a.pop() << a.pop() << endl;
return 0;
}

Here's the output:
Stack::Stack (int maximum, int count, ...)
32100

The output I'd rather have would be:
Stack::Stack (int count, ...)
43210

Edit: This also brings up an interesting order of operations issue... I just realized the numbers were printing in an order opposite of what I was expecting... a little research seems to reveal that << evaluates from right to left, but then prints from left to right?



Sydde
Jun 18, 2012, 12:21 PM
How do you expect the compiler to distinguish between the two Stack functions? It looks like it is just using the first function that matches the call. Unless you call Stack() with a single argument, the second Stack function will never be used.

ArtOfWarfare
Jun 18, 2012, 12:53 PM
How do you expect the compiler to distinguish between the two Stack functions? It looks like it is just using the first function that matches the call. Unless you call Stack() with a single argument, the second Stack function will never be used.

I recognize that that is the issue. My question is more... how do I get around it?

I decided to just completely change how memory allocation is handled by my class so it no longer has a maximum... (thus Stack(unsigned int maximum, unsigned int size, ...) isn't necessary) instead it just allocates as much as it needs initially, and then allocates a new, slightly bigger chunk of memory each time push is called, copies everything from the old memory, copies in the newly pushed value, and deletes the old memory. So no longer actually need an answer to my question.

But I'm curious if there are any good ways to get around the issue or if I'd have to go with a more Obj-C style tactic and have verbose names (i.e., in obj-c, it's not uncommon to have multiple init methods... like for NSNumber, there is initWithInt:, initWithDouble:, etc...)

chown33
Jun 18, 2012, 01:00 PM
How do I call (int, ...) instead of (int, int, ...)?

It seems you can't.

Well, maybe you can, but it looks like you'll have to trick the compiler into not matching (int,int,...). To do that, you must use an arg type other than int for one of the args, possibly with a cast.


I knew nothing about this, so I googled:
overload c++ function

which showed this as top result:
http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fcplr312.htm

This described what I already knew, but there's a link at the bottom:
Restrictions on overloaded functions (C++ only) (http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/topic/com.ibm.xlcpp8l.doc/language/ref/cplr314.htm#cplr314)

Then on the left we see:
Overload resolution (C++ only) (http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fcplr315.htm)

which links to:
Implicit conversion sequences (C++ only) (http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fimplicit_conversion_sequences.htm)

where we read that ellipsis conversions have the worst matching.


So assuming that ellipsis conversions are the worst match, there is no way to convince the compiler to call your 1-definite-type function in favor of the 2-definite-type function, except by tricking it with specific arg types. You have to make the arg types be a worse match for the 2-definite-type function. Even then, I'm not entirely sure it will call the 1-definite-type function, because the compiler might decide to promote the type rather than match ellipses.

Also, it's unclear to me what compiler you're using, what its standards compliance is, or whether the described matching of ellipses is dictated by a standard.


I think the short answer to all this is simply, "Don't do that".

I can't see a good reason for doing it the way you've done it, other than maybe testing the limits of what the compiler will recognized.

It would be a lot simpler API to understand if the constructor just defined the max size, and there was another func that did a multi-item push (if that's even necessary).

ArtOfWarfare
Jun 18, 2012, 01:08 PM
I think the short answer to all this is simply, "Don't do that".

Sounds good.

I can't see a good reason for doing it the way you've done it, other than maybe testing the limits of what the compiler will recognized.

Given I found a few ways around it, at this point, that's basically all I was doing. It seems odd that the compiler doesn't give any warnings about the fact that the one method will never be called.

Would it be possible to define my own type... like a special int that is identical to a normal int except it'll allow the compiler to know the difference between the methods?

(I'm using g++ 4.2)

chown33
Jun 18, 2012, 01:15 PM
Would it be possible to define my own type... like a special int that is identical to a normal int except it'll allow the compiler to know the difference between the methods?

See the links I posted, and look for the information on how typedefs are matched.

Sydde
Jun 18, 2012, 03:24 PM
Seems to me that a stack object should be somewhat transparent in operation. If the caller over-runs the top of a stack, it should throw an exception. The exception could either expand the stack's capacity or cause torpor or death to the caller. Pushing an uncertain amount of data seems like a very bad idea to me.

I would be inclined to include an exception vector in the object which the constructor would either default to a grow function or something like a SIGABRT. You would then add a method to set the exception vector so that the creator could handle the fault, either by suspending the faulting process until space can be cleared or by shutting that run down entirely. Obviously, this would not be quite the same as a try/catch construct.

gnasher729
Jun 18, 2012, 04:00 PM
Edit: This also brings up an interesting order of operations issue... I just realized the numbers were printing in an order opposite of what I was expecting... a little research seems to reveal that << evaluates from right to left, but then prints from left to right?

operator<< doesn't evaluate in any particular order. You have a long list of method calls, and arguments (each a.pop () is an argument to an operator<< method call). These arguments are evaluated in unspecified order. So actually the numbers could be printed in _any_ order. They could be called in different order on two consecutive calls (unlikely, but legal).