newbie questions on templates

G

Gonçalo Rodrigues

Hi all,

I'm pretty new to C++ although I have a moderate experience with
programming, especially with Python and, to a lesser extent, with Java
and C.

By necessity I needed to abandon my lovely and flexible Python for...
uh, let's just say the more difficult C++. I've been trying to
understand templates and while I understand the concept I'm not being
able to do anything useful with them.

My problem(s) can be summarized in the folowing simple example:
Suppose you are writing a bunch classes (not necessarily related by
any inheritance relationships) that provide operator== and operator!=
but the latter is always like this

bool <type>::eek:perator!=(const <type>& other) const {
return !(*this == other);
}

It would be nice to have a template that acted like a sort of mixin
and automatically introduced the above source code in the class
definition (and that's what templates are for, source code reuse).

The template is

template<typename T>
class Comparable {
public:
//Assumes operator==.
bool operator!=(const T& other) const {
return !(*this == other);
}
}

And now the class is

class Object {
public:
bool operator==(const Object& other) const {
//Compare by identity.
return this == &other;
}

The leap I'm not being able to make is how to write the Object class
so that the template Comparable gets "mixed in".

A related question: suppose now you are writing a bunch of classes
that also provide the order operators but where all of them are
defined in terms of operator< and operator==, e.g.

bool <type>::eek:perator<=(const <type>& other) const {
return (*this == other) || (*this < other);
};

and similarly for operator> and operator>=. How can I take advantage
of the Comparable template and somehow write a template that inherits
from it? Is it

template<typename T>
class Orderable : public Comparable<T> {
//etc.
}

And assuming the above is correct (that is, it does what I tried to
explain above) does it imply any inheritance relationships for its
intantiations? e.g Orderable<int> is a subclass of Comparable<int>? If
this is the case, is there any possibility of writing the template
*without* implying such relationships (without writing the template
from scratch, of course)?

TIA for any and all enlightenment you can give, with my best regards,
G. Rodrigues

P.S: Forgive me if my vocabulary is not the most canonical. As I said
I'm new to C++.

P.S: If this is not the right newsgroup to ask these type of
questions, my appologies and could anyone please direct me to a more
apropriate newsgroup?
 
A

Alf P. Steinbach

* Gonçalo Rodrigues:
The template is

template<typename T>
class Comparable {
public:
//Assumes operator==.
bool operator!=(const T& other) const {
return !(*this == other);
}
}

And now the class is

class Object {
public:
bool operator==(const Object& other) const {
//Compare by identity.
return this == &other;
}

The leap I'm not being able to make is how to write the Object class
so that the template Comparable gets "mixed in".

A related question: suppose now you are writing a bunch of classes
that also provide the order operators but where all of them are
defined in terms of operator< and operator==, e.g.

bool <type>::eek:perator<=(const <type>& other) const {
return (*this == other) || (*this < other);
};

and similarly for operator> and operator>=. How can I take advantage
of the Comparable template and somehow write a template that inherits
from it? Is it

template<typename T>
class Orderable : public Comparable<T> {
//etc.
}

Essentially yes.

But all this work has already been done.

Check out the Boost "operators" header, <url: http://www.boost.org>
(unfortunately the site seems to be down at the moment).


And assuming the above is correct (that is, it does what I tried to
explain above) does it imply any inheritance relationships for its
intantiations? e.g Orderable<int> is a subclass of Comparable<int>?
Yes.


If this is the case, is there any possibility of writing the template
*without* implying such relationships (without writing the template
from scratch, of course)?

Not really, except having those operators as free-standing functions
of two arguments (again, check out the Boost "operators" header).
 
D

Daniel T.

Gon?alo Rodrigues said:
My problem(s) can be summarized in the folowing simple example:
Suppose you are writing a bunch classes (not necessarily related by
any inheritance relationships) that provide operator== and operator!=
but the latter is always like this

bool <type>::eek:perator!=(const <type>& other) const {
return !(*this == other);
}

It would be nice to have a template that acted like a sort of mixin
and automatically introduced the above source code in the class
definition (and that's what templates are for, source code reuse).

This has already been done for you:

#include <utility>

struct Object {
//...
};

bool operator==( const Object& lhs, const Object& rhs ) {
//...
}

bool operator<( const Object& lhs, const Object& rhs ) {
//...
}

using namespace std::rel_ops;

int main() {
Object a, b;
if ( a != b ) {
std::cout << "not equal";
}
if ( a > b ) {
std::cout << "a > b";
}
}

The "using namespace std::rel_ops" line introduces the op!=, op>=, op>,
op< and op<=. Be careful though, if the operators are already defined
for a class then things can get weird.
 
R

Robbie Hatley

Gonçalo Rodrigues said:
I've been trying to understand templates and while I
understand the concept I'm not being able to do anything
useful with them.

I find templates quite useful. They're basically for cases
where code is not really type-determined. That is, for
procedures or structures that work about the same regardless
of the type of data elements involved.

An example: I sometimes write file utilities which need to
keep track of all the files in a directory. Lists, deques,
vectors, or other containers could be used to hold such
information. So did I write a separate function to
load file information into each of these types of
containers? No, I wrote just ONE template function:

// Load a list of all files in current directory matching
// a wildcard into a vector, deque, or list of some type
// of file records:
template<class Container>
Container& LoadFileList(Container& C, const string& Wildcard = "*.*")
{
ffblk FileBlock;
int done = 0;

// Create "Flags" variable and set to accept all files and
// directories, but not volume lables:
unsigned char Flags = FA_RDONLY | FA_HIDDEN | FA_SYSTEM | FA_ARCH;

// Erase all prior contents (if any) of C:
C.clear();

// Search for directory entries matching flags and wildcard:
for (done = findfirst(Wildcard.c_str(), &FileBlock, Flags);
!done;
done = findnext(&FileBlock))
{
// Try to create records of appropriate type and push them
// onto end of container:
C.push_back(typename Container::value_type(FileBlock));
}
return C;
}

This will work for any container type that has the clear() and
push_back() member functions, and any value_type that has a
constructor that takes a parameter of type ffblk. So I've
basically replaced dozens of functions with one, which is what
templates are all about.
(snip remainder)

I'll let others here more knowledgeable than I reply to your
specific queries. (Looks to me like your templates are on
the right track, though.)


--
Cheers,
Robbie Hatley
Tustin, CA, USA
email: lonewolfintj at pacbell dot net
web: home dot pacbell dot net slant earnur slant
 
D

David Rubin

Gonçalo Rodrigues said:
My problem(s) can be summarized in the folowing simple example:
Suppose you are writing a bunch classes (not necessarily related by
any inheritance relationships) that provide operator== and operator!=
but the latter is always like this
bool <type>::eek:perator!=(const <type>& other) const {
return !(*this == other);
}

'operator==' and 'operator!=' are better defined as free operators to
avoid biased implicit conversions. For example, if you have the
classes

class B {};
class A {
public:
A(B b); // create an A out of a B
bool operator==(const A& rhs);
// ...
};

then you may run into the following problem:

B x, b;
A a(x);
if(a == b) return 1; // b converts implicitly to an A
if(b == a) return 2; // Does not compile!

[summarizing]
It would be nice to have [...]
template<typename T>
class Comparable {
public:
//Assumes operator==.
bool operator!=(const T& other) const {
return !(*this == other);
}
}

[so I can do]
template<typename T>
class Orderable : public Comparable<T> {
//etc.
}

This really becomes moot point if you write your equality operators as
free operators since there is no need to ues (multiple) inheritance.
'operator!=' is almost always trivially implemented in terms of
'operator==', which in turn should only rely on public accessors.

/david
 
G

Gonçalo Rodrigues

'operator==' and 'operator!=' are better defined as free operators to
avoid biased implicit conversions. For example, if you have the
classes

class B {};
class A {
public:
A(B b); // create an A out of a B
bool operator==(const A& rhs);
// ...
};

then you may run into the following problem:

B x, b;
A a(x);
if(a == b) return 1; // b converts implicitly to an A
if(b == a) return 2; // Does not compile!

I've been readig B. Eckel's Thinking in C++ for educating myself and
he mentions this pitfall along with implicit (automatic) type
conversions.
[summarizing]
It would be nice to have [...]
template<typename T>
class Comparable {
public:
//Assumes operator==.
bool operator!=(const T& other) const {
return !(*this == other);
}
}

[so I can do]
template<typename T>
class Orderable : public Comparable<T> {
//etc.
}

This really becomes moot point if you write your equality operators as
free operators since there is no need to ues (multiple) inheritance.
'operator!=' is almost always trivially implemented in terms of
'operator==', which in turn should only rely on public accessors.

Thanx for the input, along with Alf Steinbach's answer this solves my
second question, but not the first.

The use writing operator== as methods instead of friend functions was
just the first thing that occured to me in writing *the example*. The
simple example was just that: simple and a *toy example* written to
show the problems I am having with templates.

So, repeating my first question, I do not know how to use the template

template<typename T>
class Comparable {
public:
//Assumes operator==.
bool operator!=(const T& other) const {
return !(*this == other);
}
}

in the class

class Object {
public:
bool operator==(const Object& other) const {
//Compare by identity.
return this == &other;
}

so that Object automatically gains operator!=. IOW how do I write
Object to (re)use the template Comparable?

Once again this is a toy example, operator== and operator!= are used
just to ilustrate, nothing else.

TIA, With my best regards,
G. Rodrigues
 
D

Daniel T.

Gonçalo Rodrigues said:
So, repeating my first question, I do not know how to use the template

template<typename T>
class Comparable {
public:
//Assumes operator==.
bool operator!=(const T& other) const {
return !(*this == other);
}
}

You don't need the above.
in the class

class Object {
public:
bool operator==(const Object& other) const {
//Compare by identity.
return this == &other;
}

so that Object automatically gains operator!=. IOW how do I write
Object to (re)use the template Comparable?

I already showed you. Simply put the line "using namespace std::rel_ops"
in your code. Example:

#include <utility>
using namespace std::rel_ops;

int main() {
Object a, b;
if ( a != b ) {
std::cout << "not equal";
}
if ( a > b ) {
std::cout << "a > b";
}
}
 
J

John Harrison

Daniel T. said:
You don't need the above.


I already showed you. Simply put the line "using namespace std::rel_ops"
in your code. Example:

I think maybe the point the OP is missing are that templates are more
powerful than he realises. You don't need mixin classes, you just need to
write

template <class T>
bool operator!=(const T& lhs, const T& rhs)
{
return !(lhs == rhs);
}

and template instantiation makes this code useable with any class (provided
it defines operator== of course). This is much more powerful than having to
enable this functionality in a restricted set of classes by mixing in
another class.

And as you say in this instance operator!= has already been written, but the
general point remains.

john
 
J

JKop

John Harrison posted:
template <class T>
bool operator!=(const T& lhs, const T& rhs)
{
return !(lhs == rhs);
}


How about:

template <class T, class R>
inline bool operator!=(const T& left, const R& right)
{
return !(left == right);
}


While you're at it you could define a lot more stuff, ie.
making ++i equal to i += 1 equal to i = i + 1.


-JKop
 
J

John Harrison

JKop said:
John Harrison posted:



How about:

template <class T, class R>
inline bool operator!=(const T& left, const R& right)
{
return !(left == right);
}


While you're at it you could define a lot more stuff, ie.
making ++i equal to i += 1 equal to i = i + 1.

The trouble with this very generic stuff is that it can cause ambiguity
problems. The code above (your variation or mine I can't remember which) has
already been relegated to the rel_ops namespace precisely because its habit
of breaking existing code.

But take a look at this http://www.boost.org/libs/utility/operators.htm
(good old boost again).

john
 
R

Richard Herring


I see that he gives two versions, eg:

multipliable<T, U> T operator*(const T&, const U&)
multipliable2<T, U> T operator*(const U&, const T&)


Isn't the second one redundant?
No.


For example:


template<class A,class B>
inline int Blah(A a, B b)
{
return a + b;
}


int main()
{
double a;

int b;

Blah(a,b);

Blah(b,a);

//Both work
}
[/QUOTE]
#include <iostream>

template<class A,class B>
inline A Blah(A a, B b)
{
return a + b;
}

int main()
{
double a = 1.5;
int b = 2;
std::cout << Blah(a, b) << ' ' << Blah(b, a) << '\n';
}
 
J

JKop

But they have the same return type?

I'd understand

multipliable<T, U> T operator*(const T&, const U&)
multipliable2<T, U> U operator*(const U&, const T&)


-JKop
 
K

Karl Heinz Buchegger

JKop said:
But they have the same return type?

I'd understand

multipliable<T, U> T operator*(const T&, const U&)
multipliable2<T, U> U operator*(const U&, const T&)

Now they are equivalent.

The distinction in the original version is that in the
first template T is the type of the first argument provided,
while in the second template T is the type of the second argument
provided.

rewritten for clearity

multipliable<T, U> T operator*(const T&, const U&)
multipliable2<T, U> U operator*(const T&, const U&)
 
J

John Harrison

JKop said:
But they have the same return type?

I'd understand

multipliable<T, U> T operator*(const T&, const U&)
multipliable2<T, U> U operator*(const U&, const T&)

That's the point isn't (but I've only glanced at the documentation). Assume
you have classes T and U and U is convertible to T, then you want

T t, t2;
U u;
t2 = t * u;

that is what multipliable<T, U> gives you. Also you want

T t, t2;
U u;
t2 = u * t;

and that is what multipliable2<T, U> gives you. Presumably most of the time
you'd want both.

john
 
J

JKop

Karl Heinz Buchegger posted:

rewritten for clearity

multipliable<T, U> T operator*(const T&, const U&)
multipliable2<T, U> U operator*(const T&, const U&)


I see!

C++ and computer programming in general is really good
exercise for my brain - like how a bartender develops a
really good short-term memory; have you ever noticed how 5
different drinks are called out to them from 5 different
people, and they always remember what drink to whom.


-JKop
 

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

No members online now.

Forum statistics

Threads
474,172
Messages
2,570,934
Members
47,474
Latest member
AntoniaDea

Latest Threads

Top