Can I overload operator<< using a template?

F

fungus

I have some classes which have a "writeTo()" function
but no operator<<. I want to fix it so that they
all have an operator<< (for consistency).

Can I do something like this:

template <class T>
DataDest& operator<<(DataDest& d, const T& t)
{
t.writeTo(d);
return d;
}

I tried the above but I get errors like "no operator
found which takes a right-hand operand of type XXX".

Do I need to write all the operator<< and operator>>
functions by hand?



Also, a peeve: Why can't the compiler write operator==
and operator< for me automatically? I have some structs
with about 50 members in them (mostly float values) and
to write/maintain operator==/operator< functions for
them is a pain (not to mention bug-prone).

I'm sure the compiler could do this for me automatically
in exactly the same way that it generates operator= and
copy constructors.

Is there a good reason why this wasn't done? Something
I'm missing...?


--
<\___/>
/ O O \
\_____/ FTB. For email, remove my socks.


We’re judging how a candidate will handle a nuclear
crisis by how well his staff creates campaign ads.
It’s a completely nonsensical process.
 
O

Ondra Holub

fungus napsal:
I have some classes which have a "writeTo()" function
but no operator<<. I want to fix it so that they
all have an operator<< (for consistency).

Can I do something like this:

template <class T>
DataDest& operator<<(DataDest& d, const T& t)
{
t.writeTo(d);
return d;
}

I tried the above but I get errors like "no operator
found which takes a right-hand operand of type XXX".

Do I need to write all the operator<< and operator>>
functions by hand?
It should work with template:

#include <iostream>
#include <string>

class A
{
public:
A(int data1 = 0, int data2 = 0)
: data1_(data1),
data2_(data2)
{
}

void writeTo(std::eek:stream& os) const
{
os << '[' << data1_ << ';' << data2_ << ']';
}

private:
int data1_;
int data2_;
};

template<typename T, typename S>
S& operator<<(S& d, const T& t)
{
t.writeTo(d);
return d;
}

int main(int argc,char **argv)
{
A a1(3, 5);
A a2(-1, 12);
std::string some_text(" <--> ");

std::cout << a1 << some_text << a2 << '\n';
}
Also, a peeve: Why can't the compiler write operator==
and operator< for me automatically? I have some structs
with about 50 members in them (mostly float values) and
to write/maintain operator==/operator< functions for
them is a pain (not to mention bug-prone).

I'm sure the compiler could do this for me automatically
in exactly the same way that it generates operator= and
copy constructors.

Well, it could do it, but it does not. it is designed this way, so take
it as it is.
 
R

Rolf Magnus

fungus said:
I have some classes which have a "writeTo()" function
but no operator<<. I want to fix it so that they
all have an operator<< (for consistency).

Can I do something like this:

template <class T>
DataDest& operator<<(DataDest& d, const T& t)
{
t.writeTo(d);
return d;
}

I tried the above but I get errors like "no operator
found which takes a right-hand operand of type XXX".

That should work. Your code is a bit short, so it's hard to tell why it
wouldn't. It's better to show a minimal, but complete program that exhibits
the observed behavior. The following program does compile without error
here:

class DataDest
{
};

class MyClass
{
public:
void writeTo(DataDest& d) const {}
};

template <class T>
DataDest& operator<<(DataDest& d, const T& t)
{
t.writeTo(d);
return d;
}

int main()
{
MyClass x;
DataDest d;
d << x;
}
Do I need to write all the operator<< and operator>>
functions by hand?

The template should be fine.
Also, a peeve: Why can't the compiler write operator==
and operator< for me automatically? I have some structs
with about 50 members in them (mostly float values) and
to write/maintain operator==/operator< functions for
them is a pain (not to mention bug-prone).

I'm sure the compiler could do this for me automatically
in exactly the same way that it generates operator= and
copy constructors.

Is there a good reason why this wasn't done? Something
I'm missing...?

I think that's more of a question for comp.std.c++. That newsgroup deals
more with the why than the how. Here, we just accept the C++ language as it
is. ;-)
 
N

Noah Roberts

fungus said:
Also, a peeve: Why can't the compiler write operator==
and operator< for me automatically? I have some structs
with about 50 members in them (mostly float values) and
to write/maintain operator==/operator< functions for
them is a pain (not to mention bug-prone).

Yep, that is a common problem with large, primitive obsessed objects.
You need to rethink your design here and group your data better.

If you want a language that will make your design mistakes easier to
work with you've come to the wrong place. Luckily this one isn't too
hard to fix though if you have a bunch of places where this data is
accessed directly you're going to be there for a while.
 
M

mlimber

Noah said:
Yep, that is a common problem with large, primitive obsessed objects.
You need to rethink your design here and group your data better.

Besides, what is the compiler supposed to do with pointer members?
Should it compare values or pointed-to-addresses? If values, what
should it do if one or both pointers is null? What if a pointer is
simply uninitialized? (Answer: you've got a very subtle, hard-to-find
bug on your hands because it's hidden in implicit code.)

Cheers! --M
 
J

Jacek Dziedzic

Also, a peeve: Why can't the compiler write operator==
and operator< for me automatically? I have some structs
with about 50 members in them (mostly float values) and
to write/maintain operator==/operator< functions for
them is a pain (not to mention bug-prone).

I'm sure the compiler could do this for me automatically
in exactly the same way that it generates operator= and
copy constructors.

Is there a good reason why this wasn't done? Something
I'm missing...?

Concerning operator<:

struct foo {
std::string employee_name;
Date date_hired;
// ...
};

foo employers[2]=//...

if(employers[0]>employers[1]) {
// how does the compiler know whether to
// compare by employee_name or by date_hired?
}

HTH,
- J.
 
R

Rolf Magnus

I can see that it could be useful to have an auto-generated operator==, but
how would the compiler generate operator<? When is an object 'less' than
another? If all members are 'less'? If one member is 'less'? Which one
then? Or should it do a lexographical comparison? Which order should it use
in that case?
Besides, what is the compiler supposed to do with pointer members?
Should it compare values or pointed-to-addresses?

It should compare the member's value, and since the member is a pointer,
that means the address. The language should never implicitly dereference a
pointer.
If values, what should it do if one or both pointers is null? What if a
pointer is simply uninitialized? (Answer: you've got a very subtle,
hard-to-find bug on your hands because it's hidden in implicit code.)

How does that differ from the assignment operator, which is
compiler-generated by default?
 
F

fungus

Rolf said:
That should work. Your code is a bit short, so it's hard to tell why it
wouldn't.

I'll have another play with it and see what happens
It's better to show a minimal, but complete program that exhibits
the observed behavior.

- but the code is so simple...(!)

Yep, that is a common problem with large, primitive obsessed objects.
You need to rethink your design here and group your data better.

If you want a language that will make your design mistakes easier to
work with you've come to the wrong place.

I have a class which represents a "material".
Materials have a lot of properties, there's no
way around that. I want to put materials in a
std::map for lookup so I need an operator<. I
need to compare two materials so I need an
operator=.

I don't see how there's a "design flaw" here.


--
<\___/>
/ O O \
\_____/ FTB. For email, remove my socks.


We’re judging how a candidate will handle a nuclear
crisis by how well his staff creates campaign ads.
It’s a completely nonsensical process.
 
F

fungus

mlimber said:
what is the compiler supposed to do with pointer members?
Should it compare values or pointed-to-addresses? If values, what
should it do if one or both pointers is null? What if a pointer is
simply uninitialized?

You have exactly the same problem with assignment
operator and copy constructor but the compiler
seems happy enough to do those for me.

--
<\___/>
/ O O \
\_____/ FTB. For email, remove my socks.


We’re judging how a candidate will handle a nuclear
crisis by how well his staff creates campaign ads.
It’s a completely nonsensical process.
 
F

fungus

Rolf said:
how would the compiler generate operator<?

The code I'm writing at the moment looks like this:


struct item {
float value1;
float value2;
...
};

bool operator<(const item& a, const item& b)
{
// value1
if (a.value1 < b.value1) return true;
if (b.value1 < a.value1) return false;

// value2
if (a.value2 < b.value2) return true;
if (b.value2 < a.value2) return false;

// ...

// If we reach here then a and b are equal
return false;
}


--
<\___/>
/ O O \
\_____/ FTB. For email, remove my socks.


We’re judging how a candidate will handle a nuclear
crisis by how well his staff creates campaign ads.
It’s a completely nonsensical process.
 
F

fungus

Jacek said:
// how does the compiler know whether to
// compare by employee_name or by date_hired?

If I want specifics like these then I'll write
appropriate code but I can think of many
situations where all I want is generic compares
and fast lookups (eg. std::map, std::lower_bound).


--
<\___/>
/ O O \
\_____/ FTB. For email, remove my socks.


We’re judging how a candidate will handle a nuclear
crisis by how well his staff creates campaign ads.
It’s a completely nonsensical process.
 
F

fungus

fungus said:
I'll have another play with it and see what happens...

I figured it out - it's all to do with namespaces.

Here's a "broken" piece of code:

#include <iostream>
#include <string>

namespace foo {
class A {
public:
void writeTo(std::eek:stream& os) const {
os << "A";
}
};
void operator<<(std::eek:stream& os, const class B&);
void test();
}

template<typename T, typename S>
S& operator<<(S& d, const T& t)
{
t.writeTo(d);
return d;
}

void foo::test()
{
A a;
std::cout << a << '\n';
}

int main(int argc,char **argv)
{
foo::test();
}


The operator<< in namespace foo is hiding the template
operator<< (which is in the global namespace).


--
<\___/>
/ O O \
\_____/ FTB. For email, remove my socks.


We’re judging how a candidate will handle a nuclear
crisis by how well his staff creates campaign ads.
It’s a completely nonsensical process.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top