Sign in to follow this  

Overloading postfix increment/decrement operator, accept type other than int?

This topic is 3743 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

I've been trying to overload postfix increment/decrement operators with a type other than int, but everything I see indicates that this cannot be done. Apparently the form must have an 'int' argument as this is the only thing that the compiler will recognize. Is anyone aware of a way around this? ex:
// Case 1: Standard method...
class myClass
{
myClass operator--(int i)
{
    myClass copy(*this);
    --value_;
    return copy;
}
};

// Case 2: Desired method...
class myClass
{
myClass operator--(myClass& m)
{
    myClass copy(*this);
    --m.value_;
    return copy;
}
};
--random

Share this post


Link to post
Share on other sites
Unary overloaded operators inside a class declaration do not take an explicit argument (as the "this" pointer suffices).

operator--() should work, but will only overload the pre-decrement version.

When faced with the what the post-decrement operators function signature should look like, the C++ standards writers basically cheated. If you declare operator--(int) in your class then that code will be called when you try to call foo--; where foo is an object of MyClass. The int has no meaning (other than to disambiguate which operator you are overloading).


struct Foo {
Foo operator++(int) { Foo temp(*this);/* do work */ return temp; }
};

void Example( Foo foo ) {
foo++;
}




Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Why would you want to?


Hi Si,

I don't know if you saw in another thread, but I was experimenting with a safe integer type for iteration, that could overcome potentially unsafe integer assignments, catch divide by zero buffer overflow conditions, incorrect array indexing, incorrect iteration values, etc. This quickly led to these postfix overload issues, and some other issues such as type conversion behaviour.

At the moment I'm unable to post decrement without manually inserting the code for each occurrence. I suppose that a function of some sort could be used...

--random

Share this post


Link to post
Share on other sites
Quote:
Original post by random_thinker
I've been trying to overload postfix increment/decrement operators with a type other than int, but everything I see indicates that this cannot be done. Apparently the form must have an 'int' argument as this is the only thing that the compiler will recognize. Is anyone aware of a way around this?


There is no way around it. It's a part of the language definition. The actual argument is never used and there's no way you can pass a value to it, it's just an artefact of how you specify postdecrement operators.

--smw

Share this post


Link to post
Share on other sites
Yeah, I think you may be slightly confused as to the purpose of the int. It is never used at all. They may as well have chosen bool or void*. It's only there so that the argument list for postincrement is different from the argument list for preincrement. There's no reason to use anything else.

Share this post


Link to post
Share on other sites
From what I've read, it's a little bit of a compiler hack, in that where it sees the int it inserts a zero and the proceeds with the overload. But I was wondering if the post increment/decrement could take arguments like custom ostream formatter functions do. I have seen that you can also have non-member post increment/decrement operator functions that will take a class as an arguement, something like:


myClass operator++(myClass& mc, int i)
{
// do the post increment thingy...
}


But I was hoping that there might be some obscure work-around.

--random

Share this post


Link to post
Share on other sites
I don't think I understand what you are asking. What would the client code for such an overloaded ++ operator look like?

Give an example of the code you would like to compile, client code and all and then I might have some idea.

Until then...

Share this post


Link to post
Share on other sites
Quote:
Original post by random_thinker
From what I've read, it's a little bit of a compiler hack, in that where it sees the int it inserts a zero and the proceeds with the overload. But I was wondering if the post increment/decrement could take arguments like custom ostream formatter functions do.

For ostream, << is a binary operator. How would ++ take an extra argument? It's a unary operator. Where would you shove the other argument... between the two plus signs?

Share this post


Link to post
Share on other sites
Here's a snippit of the relevant code...


template<intType>
class int_t
{
public:
// All the ctor/dtor/assignment stuff

// Increment. This works fine.
int_t operator++()
{
++value_;
return *this;
}

// Post Increment. This does not work because the
// type conversion operator is private.
int_t operator++(intType)
{
int_t<intType>copy(*this);
++value_;
return copy;
}

private:
intType value_;

// Making this private stops a lot of the conversion dangers
// associated with unsigned signed integer conversion.
operator intType()
{
}
};




You can see some of the problems. I suppose that this can be handled with an external function or method but it is a little bit strange to me.--

--random

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Quote:
Original post by random_thinker
From what I've read, it's a little bit of a compiler hack, in that where it sees the int it inserts a zero and the proceeds with the overload. But I was wondering if the post increment/decrement could take arguments like custom ostream formatter functions do.

For ostream, << is a binary operator. How would ++ take an extra argument? It's a unary operator. Where would you shove the other argument... between the two plus signs?


Yeah I know but I was wondering if the pattern can be used...when overloaded as a separate function I have read that the post increment operator does have a form similar to custom ostream formatters, but still must take an int, as in 'myClass operator++(myClass&,int)'...

--random

Share this post


Link to post
Share on other sites
Whaaa? Seriously, just change the argument to the postincrement operator to int. Look, I don't know if I can put this any more clearly. The type of the argument to operator++ has nothing to do with integers, or with anything at all other than disambiguation. It just happens to be int because that's the type that Bjarne happened to pull out of his ass. You can't actually USE the argument for anything.

How would you like to *use* the strangely-overloaded postincrement operator? Show me some example code which actually uses it.

Share this post


Link to post
Share on other sites
Thanks Sneftel..I've changed and recompiled and it works. What a compiler hack this is; just an int, and I thought that any integer would do, because, as I understand, only integral types can use increment operators anyway.

I've used this int_t (safe integer) class pretty extensively over the last day or so and it works quite well. Now I'm trying to push it to the bit level, thinking that this will be the ultimate test of the type...the code follows:


// int_t safe integer type
template<typename intType>
class int_t
{
public:
int_t() :
value_(0)
{
}

int_t(const intType arg) :
value_(arg)
{
}

int_t(const int_t<intType>& arg) :
value_(arg.value_)
{
}

~int_t()
{
}

int_t operator=(const int_t<intType> arg)
{
value_ = arg.value_;
return *this;
}

int_t operator=(const intType arg)
{
value_ = arg;
return *this;
}

int_t operator+(const intType arg) const
{
return (int_t<intType>(value_ + arg));
}

int_t operator+(const int_t<intType> arg) const
{
return (int_t<intType>(value_ + arg.value_));
}

int_t operator-(const intType arg) const
{
return (int_t<intType>(value_ - arg));
}

int_t operator-(const int_t<intType> arg) const
{
return (int_t<intType>(value_ - arg.value_));
}

int_t operator*(const intType arg) const
{
return (int_t<intType>(value_ * arg));
}

int_t operator*(const int_t<intType> arg) const
{
return (int_t<intType>(value_ * arg.value_));
}

int_t operator/(const intType arg) const
{
if (arg == 0)
{
// Custom error class...
STUtility::error error_cond("Divide by zero");
error_cond.die();
}
return (int_t<intType>(value_ / arg));
}

int_t operator/(const int_t<intType> arg) const
{
if (arg.value_ == 0)
{
// Custom error class...
STUtility::error error_cond("Divide by zero");
error_cond.die();
}
return (int_t<intType>(value_ / arg.value_));
}

int_t operator%(const intType arg) const
{
return (int_t<intType>(value_ % arg));
}

int_t operator%(const int_t<intType> arg) const
{
return (int_t<intType>(value_ % arg.value_));
}

int_t operator&(const intType arg) const
{
return (int_t<intType>(value_ & arg));
}

int_t operator&(const int_t<intType> arg) const
{
return (int_t<intType>(value_ & arg.value_));
}

int_t operator|(const intType arg) const
{
return (int_t<intType>(value_ | arg));
}

int_t operator|(const int_t<intType> arg) const
{
return (int_t<intType>(value_ | arg.value_));
}

int_t operator^(const intType arg) const
{
return (int_t<intType>(value_ ^ arg));
}

int_t operator^(const int_t<intType> arg) const
{
return (int_t<intType>(value_ ^ arg.value_));
}

int_t operator~() const
{
return (int_t<intType>(~value_));
}

int_t operator<<(const intType arg) const
{
return (int_t<intType>(value_ << arg));
}

int_t operator<<(const int_t<intType> arg) const
{
return (int_t<intType>(value_ << arg.value_));
}

int_t operator>>(const intType arg) const
{
return (int_t<intType>(value_ >> arg));
}

int_t operator>>(const int_t<intType> arg) const
{
return (int_t<intType>(value_ >> arg.value_));
}

// Increment.
int_t operator++()
{
++value_;
return *this;
}

// Post increment.
int_t operator++(int)
{
int_t<intType>copy(*this);
++value_;
return copy;
}

// Decrement.
int_t operator--()
{
--value_;
return *this;
}

// Post decrement.
int_t operator--(int)
{
int_<intType>copy(*this);
--value_;
return copy;
}

int_t operator+=(const intType arg)
{
value_ = value_ + arg;
return *this;
}

int_t operator+=(const int_t<intType> arg)
{
value_ = value_ + arg.value_;
return *this;
}

int_t operator-=(const intType arg)
{
value_ = value_ - arg;
return *this;
}

int_t operator-=(const int_t<intType> arg)
{
value_ = value_ - arg.value_;
return *this;
}

int_t operator*=(const intType arg)
{
value_ = value_ * arg;
return *this;
}

int_t operator*=(const int_t<intType> arg)
{
value_ = value_ * arg.value_;
return *this;
}

int_t operator/=(const intType arg)
{
if (arg == 0.0)
{
// Custom error class...
STUtility::error error_cond("Divide by zero");
error_cond.die();
}
value_ = value_ / arg;
return *this;
}

int_t operator/=(const int_t<intType> arg)
{
if (arg.value_ == 0.0)
{
// Custom error class...
STUtility::error error_cond("Divide by zero");
error_cond.die();
}
value_ = value_ / arg.value_;
return *this;
}

intType value()
{
return value_;
}

bool operator==(const intType arg) const
{
return (value_ == arg);
}

bool operator==(const int_t<intType> arg) const
{
return (value_ == arg.value_);
}

bool operator!=(const intType arg) const
{
return (value_ != arg);
}

bool operator!=(const int_t<intType> arg) const
{
return (value_ != arg.value_);
}

bool operator>=(const intType arg) const
{
return (value_ >= arg);
}

bool operator>=(const int_t<intType> arg) const
{
return (value_ >= arg.value_);
}

bool operator>(const intType arg) const
{
return (value_ > arg);
}

bool operator>(const int_t<intType> arg) const
{
return (value_ > arg.value_);
}

bool operator<=(const intType arg) const
{
return (value_ <= arg);
}

bool operator<=(const int_t<intType> arg) const
{
return (value_ <= arg.value_);
}

bool operator<(const intType arg) const
{
return (value_ < arg);
}

bool operator<(const int_t<intType> arg) const
{
return (value_ < arg.value_);
}

static intType min()
{
return std::numeric_limits<intType>::min();
}

static intType max()
{
return std::numeric_limits<intType>::max();
}

private:
intType value_;
operator intType() const
{
return value_;
}
};

// ostream operator for int_t
template<typename intType>
std::ostream & operator<<(std::ostream & out, int_t<intType> arg)
{
return ( out << arg.value() );
}

// istream operator for int_t
template<typename intType>
std::istream & operator>>(std::istream & in, int_t<intType> arg)
{
return ( in >> arg.value() );
}






I'm now trying to see if it can be used for number generators, and this is where I'm starting to have problems. Some usage code follows and this just is not working yet, because it is mixing different int_t types, and the conversion behaviour at this moment is completely locked.


// The main generator method definition...this is based upon code from Marsaglia (2003).
// safe_intu32 is a typedef for int_t<unsigned long>
// safe_intu64 is a typedef for int_t<unsigned long long>
// types are:
// safe_intu32 i_,c_,x_,r_
// safe_intu64 a_,t_
// std::vector<safe_intu32> Q_
//
inline STBase::safe_intu32 STRandom::G0064::next()
{
i_ = (i_ + 1)&4095; // Error, ambiguous conversion
t_ = a_ * Q_[i_]+ c_; // Error, ambiguous overload operator *
c_ = (t_ >> 32); // Error, ambiguous conversion
x_ = t_ + c_; // Error, ambiguous overload operator +
if (x_ < c_) // OK here
{
x_++; // Now this is OK...thanks!
c_++; // Ditto.
}
return (Q_[i_] = r_ - x_); // Some problem here.
}





The rest of these error seem to be related to the way that the compiler is interpreting the types and their conversion, and my first thought is to handle this problem through specialization and overloading conversion/assignment. Another approach could be to introduce a type conversion behaviour that is used by this base class.

--random

PS If this idea were not crazy enough, I've also introduced a 'real_t' class template that uses tolerances as a proportion of magnitude (as set in the real_limits struct) for boolean operations. It was actually much easier to implement than int_t.

Edit...looking at the code of Marsaglia above, the increment operators used should be pre- rather than post- anyway!


[Edited by - random_thinker on September 18, 2007 4:26:52 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by random_thinker
Thanks Sneftel..I've changed and recompiled and it works. What a compiler hack this is; just an int, and I thought that any integer would do, because, as I understand, only integral types can use increment operators anyway.


Arg! So much confusion :)

The int does nothing. It is merely a means to distinguish between pre- and post-decrement. How would you pass in an argument to the pre- or post-decrement overload at the call site, anyway?!

Any non-primitive type can overload ++ and --. STL iterators do, for example. They aren't integers. Neither are arbitrary precision integer objects or semaphores, implementations of which routinely overload ++ and --.

Share this post


Link to post
Share on other sites
Quote:
Original post by the_edd
Quote:
Original post by random_thinker
Thanks Sneftel..I've changed and recompiled and it works. What a compiler hack this is; just an int, and I thought that any integer would do, because, as I understand, only integral types can use increment operators anyway.


Arg! So much confusion :)

The int does nothing. It is merely a means to distinguish between pre- and post-decrement. How would you pass in an argument to the pre- or post-decrement overload at the call site, anyway?!

Any non-primitive type can overload ++ and --. STL iterators do, for example. They aren't integers. Neither are arbitrary precision integer objects or semaphores, implementations of which routinely overload ++ and --.


Thanks Edd, I get the message...I know that any class/struct definition can use these operators, but my mind is tuned in to primitive types at the moment, in the above statement I should have instead said "because, as I understand, only integral types of the primitives can use increment operators anyway"

I recognize now that the 'int' is just a 'marker' for the compiler. As much as I love to work with C++, it's full of these little nuances that, on the surface, don't seem to have any logic to me. For example, sending a type (int) as an argument just to make a marker for the compiler. Anyway, I guess all languages have these issues.

--random

Share this post


Link to post
Share on other sites

This topic is 3743 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this