Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

map of std::complex

1 view
Skip to first unread message

Mohamed Ali

unread,
Dec 28, 2008, 5:54:04 PM12/28/08
to
I need to create a map:
map<complex<int>,complex<double> >
But it generate an error due to operator != and < ?
Am I supposed to overload != and < operator for std::complex and How?

Thanks for help

Joe Smith

unread,
Dec 28, 2008, 6:13:46 PM12/28/08
to

"Mohamed Ali" <cher...@gmail.com> wrote in message
news:ea197a0d-5038-4d0c...@q26g2000prq.googlegroups.com...

You could use code like the following:

template<typename T>
struct complex_less
{
bool operator() (const std::complex<T>& lhs, const std::complex<T>& rhs)
{
if (lhs.real()<rhs.real()) return true;
else if (lhs.real()==rhs.real()&& lhs.imag()<rhs.imag()) return true;
return false;
}
};

std::map<std::complex<int>,std::complex<double>, complex_less<int> > t;


peter koch

unread,
Dec 28, 2008, 7:01:25 PM12/28/08
to
On 28 Dec., 23:54, Mohamed Ali <chermi...@gmail.com> wrote:
> I need to create a map:
> map<complex<int>,complex<double> >

Why? I've never seen complex used as key, and I also find complex<int>
slightly unusual. This does not exclude the possibility that your
design is wrong, of course.

> But it generate an error due to operator != and < ?

I can't imagine it complaining about a missing operator !=. But you
need to supply an operator <, as complex numbers aren't ordered.

> Am I supposed to overload != and < operator for std::complex and How?

You are supposed to create an ordering function. As to how, I believe
you should do some of your work yourself and search for the answer in
your textbook, the net (e.g. the C++ FAQ) or in your compilers
documentation.

/Peter

Juha Nieminen

unread,
Dec 28, 2008, 7:08:52 PM12/28/08
to
Joe Smith wrote:
> if (lhs.real()<rhs.real()) return true;
> else if (lhs.real()==rhs.real()&& lhs.imag()<rhs.imag()) return true;
> return false;

Wouldn't that be the same as:

return lhs.real() < rhs.real() ||
(lhs.real() == rhs.real() && lhs.imag() < rhs.imag());

blargg

unread,
Dec 28, 2008, 7:45:41 PM12/28/08
to

Yes. I think Joe Smith was attempting a more general pattern where the
number of members can vary, since using a single expression for
everything can get unwieldy. Something like this:

struct T
{
A a;
B b;
...
};

struct Comp
{
bool operator () ( T const& x, T const& y ) const
{
if ( x.a < y.a ) return true;
if ( x.a > y.a ) return false;

if ( x.b < y.b ) return true;
if ( x.b > y.b ) return false;

...

return false;
}
};

But of course making a map of something with floating-point keys is a
bound to cause problems if the keys are generated by more than one
calculation.

James Kanze

unread,
Dec 29, 2008, 4:45:32 AM12/29/08
to

Or even the canonical:

return lhs.real() < rhs.real() ||
(!(rhs.real() < lhs.real()) && lhs.imag() < rhs.imag());

(Personally, I prefer your version, and find it more natural.
But the above is what the standard library usually uses.)

Also, I wouldn't name it complex_less, or anything that might
suggest that it has anything to do with less than in a
mathematical sense. Something like complex_ordering, or
something which suggests an arbitrary ordering would probably be
better. (But admittedly, it's not a big point, as long as he
doesn't use it to overload <.)

Another alternative which is sometimes suggested is the name it
std::less< std::complex< double> > or whatever the desired
instantiation is. Do this, and you don't have to specify the
comparison object when instantiating std::map. Of course, you
do have to ensure that you're the only coder in the project
which does it, or you'll probably run afowl of the one
definition rule, so you probably wouldn't want to do it in
anything but toy code.

--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Juha Nieminen

unread,
Dec 29, 2008, 5:52:46 AM12/29/08
to
James Kanze wrote:
> Or even the canonical:
>
> return lhs.real() < rhs.real() ||
> (!(rhs.real() < lhs.real()) && lhs.imag() < rhs.imag());
>
> (Personally, I prefer your version, and find it more natural.
> But the above is what the standard library usually uses.)

I suppose the idea in the standard library is to avoid unnecessarily
requiring the type to support operator==, when operator< will suffice
equally well?

Pete Becker

unread,
Dec 29, 2008, 7:03:47 AM12/29/08
to
On 2008-12-29 04:45:32 -0500, James Kanze <james...@gmail.com> said:

>
> Another alternative which is sometimes suggested is the name it
> std::less< std::complex< double> > or whatever the desired
> instantiation is. Do this, and you don't have to specify the
> comparison object when instantiating std::map. Of course, you
> do have to ensure that you're the only coder in the project
> which does it, or you'll probably run afowl of the one
> definition rule, so you probably wouldn't want to do it in
> anything but toy code.

Even then, the behavior is undefined. It's a specialization of a
standard library template but it doesn't depend on any user-defined
types.

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

James Kanze

unread,
Dec 29, 2008, 3:07:49 PM12/29/08
to

That's the theory. I'll let you guess as to what I think with
regards to types that define operator<, but not operator==.

James Kanze

unread,
Dec 29, 2008, 3:09:01 PM12/29/08
to
On Dec 29, 1:03 pm, Pete Becker <p...@versatilecoding.com> wrote:

> On 2008-12-29 04:45:32 -0500, James Kanze <james.ka...@gmail.com> said:
> > Another alternative which is sometimes suggested is the name
> > it std::less< std::complex< double> > or whatever the
> > desired instantiation is. Do this, and you don't have to
> > specify the comparison object when instantiating std::map.
> > Of course, you do have to ensure that you're the only coder
> > in the project which does it, or you'll probably run afowl
> > of the one definition rule, so you probably wouldn't want to
> > do it in anything but toy code.

> Even then, the behavior is undefined. It's a specialization of
> a standard library template but it doesn't depend on any
> user-defined types.

Good point. So you'd have to specialize it on std::complex<
MyDouble >, or something like that.

Joe Smith

unread,
Dec 29, 2008, 5:31:35 PM12/29/08
to

"James Kanze" wrote:
>On Dec 29, 1:08 am, Juha Nieminen <nos...@thanks.invalid> wrote:
>> Joe Smith wrote:
>> > if (lhs.real()<rhs.real()) return true;
>> > else if (lhs.real()==rhs.real()&& lhs.imag()<rhs.imag()) return
>> > true;
>> > return false;
>
>> Wouldn't that be the same as:
>>
>> return lhs.real() < rhs.real() ||
>> (lhs.real() == rhs.real() && lhs.imag() < rhs.imag());
>
>Or even the canonical:
>
> return lhs.real() < rhs.real() ||
> (!(rhs.real() < lhs.real()) && lhs.imag() < rhs.imag());
>
>(Personally, I prefer your version, and find it more natural.
>But the above is what the standard library usually uses.)

His suggestion is the most natural, but the latter has the advantage
of not needing a working ==.

They are also not equivlent: lhs as 6+6i and rhs as 5+7i gives different
results for the two.

>Also, I wouldn't name it complex_less, or anything that might
>suggest that it has anything to do with less than in a
>mathematical sense. Something like complex_ordering, or
>something which suggests an arbitrary ordering would probably be
>better. (But admittedly, it's not a big point, as long as he
>doesn't use it to overload <.)

Indeed. My example was just a quick and dirty example, which is why it was
not a one-line function, and used a questionable name.


Joe Smith

unread,
Dec 29, 2008, 6:09:34 PM12/29/08
to

James Kanze wrote:
>On Dec 29, 1:03 pm, Pete Becker <p...@versatilecoding.com> wrote:
>> Even then, the behavior is undefined. It's a specialization of
>> a standard library template but it doesn't depend on any
>> user-defined types.
>
>Good point. So you'd have to specialize it on std::complex<
>MyDouble >, or something like that.

The only issue is that the behavior of std::complex<T> for a T that is not
float, double, or long double is unspecified.

That seems odd. The correct behavior for std::complex<int> for example is
obvious, (at least for the arithmatic functions) and there seems to be no
reason why that should be unspecified.

The case of std::complex<UserDefinedFloatingPointType> should be
specifiable, too, right? Perhaps I should read back over the std::complex
related proposals for C++0x that were rejected or deferred.

As a slightly related aside, I just noticed that [complex.numbers]/4 in
N2800 has some slight typographical issues, namely that in several places cv
was typeset in bold, when it should have been typeset in italic. I mention
this here, because I'm not sure how best to report this, but am confident
that somebody who does will see it.

James Kanze

unread,
Dec 30, 2008, 5:01:52 AM12/30/08
to
On Dec 29, 11:31 pm, "Joe Smith" <unknown_kev_...@hotmail.com> wrote:
> "James Kanze" wrote:
> >On Dec 29, 1:08 am, Juha Nieminen <nos...@thanks.invalid> wrote:
> >> Joe Smith wrote:
> >> > if (lhs.real()<rhs.real()) return true;
> >> > else if (lhs.real()==rhs.real()&& lhs.imag()<rhs.imag()) return
> >> > true;
> >> > return false;

> >> Wouldn't that be the same as:

> >> return lhs.real() < rhs.real() ||
> >> (lhs.real() == rhs.real() && lhs.imag() < rhs.imag());

> >Or even the canonical:

> > return lhs.real() < rhs.real() ||
> > (!(rhs.real() < lhs.real()) && lhs.imag() < rhs.imag());

> >(Personally, I prefer your version, and find it more natural.
> >But the above is what the standard library usually uses.)

> His suggestion is the most natural, but the latter has the
> advantage of not needing a working ==.

Do you have some types where < is defined, but there is no
working ==? Such types wouldn't get through code review at the
places I've worked.

> They are also not equivlent: lhs as 6+6i and rhs as 5+7i gives
> different results for the two.

They both evaluate to false, which is what I'd expect. If they
do give different results, the definitions of < and == aren't
compatible.

Juha Nieminen

unread,
Dec 30, 2008, 6:36:26 AM12/30/08
to
Joe Smith wrote:
>> return lhs.real() < rhs.real() ||
>> (!(rhs.real() < lhs.real()) && lhs.imag() < rhs.imag());

> His suggestion is the most natural, but the latter has the advantage


> of not needing a working ==.

It might also be possible that the version using operator< only can be
better optimized by the compiler.

If you call lhs.real() < rhs.real() and then lhs.real == rhs.real(),
it's probable that the compiler will produce two comparisons and two
conditional jumps from those.

However, if you call lhs.real() < rhs.real() and then
!(lhs.real() < rhs.real()), it's theoretically possible that the
compiler will notice the pattern and create only one comparison and one
conditional jump.

Kai-Uwe Bux

unread,
Dec 30, 2008, 6:45:44 AM12/30/08
to
Juha Nieminen wrote:

Huh? You have the first comparison

lhs.real() < rhs.real()

and the second comparison with reversed order of operands

rhs.real() < lhs.real()

whose result is negated. I don't see how the compiler could optimize one
comparison away (unless there is a machine instruction doing a three-way
comparison).

On the other hand, I suspect that a good optimizing compiler might generate
identical code regardless of the form the programmer chooses.


Best

Kai-Uwe Bux

James Kanze

unread,
Dec 30, 2008, 11:52:02 AM12/30/08
to
On Dec 30, 12:36 pm, Juha Nieminen <nos...@thanks.invalid> wrote:
> Joe Smith wrote:
> >> return lhs.real() < rhs.real() ||
> >> (!(rhs.real() < lhs.real()) && lhs.imag() < rhs.imag());
> > His suggestion is the most natural, but the latter has the
> > advantage of not needing a working ==.

> It might also be possible that the version using operator<
> only can be better optimized by the compiler.

Or the reverse might also be possible.

> If you call lhs.real() < rhs.real() and then lhs.real ==
> rhs.real(), it's probable that the compiler will produce two
> comparisons and two conditional jumps from those.

On most machines, a comparison will set status bits; the
compiler will likely see that the comparison has already been
done, and only test the status bits. Of course, the compiler
could also recognize that !x<y is the same as x>y, and recognize
that the status bits were already set for that as well. With a
good optimizing compiler, I'd expect no difference.

> However, if you call lhs.real() < rhs.real() and then
> !(lhs.real() < rhs.real()), it's theoretically possible that
> the compiler will notice the pattern and create only one
> comparison and one conditional jump.

Or not. Back in the old days, Fortran had a three way if
(arithmetic if), which didn't test for true/false, but for
positive/zero/negative. I would expect that handling such cases
would involve well known optimization techniques. (Curiously,
both g++ and VC++ seem to use three comparison instructions in
both cases. On the AMD machine where I'm writing this, I'd have
expected something along the lines of:
xor %eax, %eax
ucomisd %xmm0, %xmm2
jnz $end
ucomisd %xmm1, %xmm3
$end:
addc 0, $eal
rep
for either of them, supposing I understand the instruction set
correctly---I've never really studied it. G++ is far from that.
(The issue on the 32 bit Intel architecture on which I have VC++
is more complex, because floating point comparison doesn't
directly affect the CPU status codes. Still, VC++ compares and
reads the status codes twice for the first double, where once
would definitely suffice.)

Joe Smith

unread,
Jan 1, 2009, 12:34:19 AM1/1/09
to

James Kanze wrote:
>On Dec 29, 11:31 pm, "Joe Smith" <unknown_kev_...@hotmail.com> wrote:
>> "James Kanze" wrote:
>> >On Dec 29, 1:08 am, Juha Nieminen <nos...@thanks.invalid> wrote:
>> >> Wouldn't that be the same as:
>> >> return lhs.real() < rhs.real() ||
>> >> (lhs.real() == rhs.real() && lhs.imag() < rhs.imag());
>
>> >Or even the canonical:
>
>> > return lhs.real() < rhs.real() ||
>> > (!(rhs.real() < lhs.real()) && lhs.imag() < rhs.imag());
>
>> >(Personally, I prefer your version, and find it more natural.
>> >But the above is what the standard library usually uses.)
>
>> His suggestion is the most natural, but the latter has the
>> advantage of not needing a working ==.
>
>Do you have some types where < is defined, but there is no
>working ==? Such types wouldn't get through code review at the
>places I've worked.
>
>> They are also not equivlent: lhs as 6+6i and rhs as 5+7i gives
>> different results for the two.
>
>They both evaluate to false, which is what I'd expect. If they
>do give different results, the definitions of < and == aren't
>compatible.
>

Right. I origninally misread the second example,
as

return lhs.real() < rhs.real() ||
(!(LHS.real() < RHS.real()) && lhs.imag() < rhs.imag());

(Caps for emphasis).

0 new messages