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

Started by
14 comments, last by random_thinker 16 years, 7 months ago
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
--random_thinkerAs Albert Einstein said: 'Imagination is more important than knowledge'. Of course, he also said: 'If I had only known, I would have been a locksmith'.
Advertisement
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
--random_thinkerAs Albert Einstein said: 'Imagination is more important than knowledge'. Of course, he also said: 'If I had only known, I would have been a locksmith'.
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.
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 typetemplate<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_ttemplate<typename intType>std::ostream & operator<<(std::ostream & out, int_t<intType> arg){	return ( out << arg.value() ); }// istream operator for int_ttemplate<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]
--random_thinkerAs Albert Einstein said: 'Imagination is more important than knowledge'. Of course, he also said: 'If I had only known, I would have been a locksmith'.
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 --.
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
--random_thinkerAs Albert Einstein said: 'Imagination is more important than knowledge'. Of course, he also said: 'If I had only known, I would have been a locksmith'.

This topic is closed to new replies.

Advertisement