Operator overloading with templates

A

allan.mcrae

I am having trouble with overloading the += operator when template
parameters are used. I have a class holding an array (called "derived"
in the following example) which derives from a base class ("base"). I
want to be able to add:

1) any derived array holding class to any other derived array holding
class
2) any derived array holding class to a literal value (e.g int, double,
etc) for which addition is defined for the type in the array.

I would like both + and += operators and for appropriate casting to be
handled. A minimal example of what I am trying to do is below (note
that the derived class holds only one value for simplicity).

The two + operators work fine with GCC (versions 3.4 and 4.1). But the
two += don't work when defined together (they work fine when defined
one at a time). When performing derived<L> += derived<R> the
operator+=(base<L,D>, R) is selected rather than operator+=(base<L,LD>,
base<R,RD>). Borland C++ Builder has this problem and a similar one
with the the binary addtion operator. I am really confused about GCC
because the operator prototypes are practically the same, so it would
seem that the the problem += and + operators are either both correct or
wrong.

Using operator+=(base<L,D>, L) works, but without automatic casting I
am after. Any suggestions on how to get around this problem, at least
in GCC would be great!

Example program (88 lines):


#include <iostream>

// curiously recurring base class
template <typename T, typename D>
class base
{
public:
T& operator[](std::size_t i) { return static_cast<D&>(*this); }
const T& operator[](std::size_t i) const { return static_cast<const
D&>(*this); }

};

// curiously recurring derived class
template <typename T>
class derived : public base<T, derived<T> >
{
public:
derived(T t) : t_(t) {}
T& operator[](std::size_t i) {return t_; }
const T& operator[](std::size_t i) const {return t_; }
private:
T t_;
};


// type promotion trait
template <typename L, typename R>
class promote
{
};

template<>
class promote<int, double>
{
public:
typedef double type;
};


// binary addition operators with casting
template <typename L, typename D, typename R>
derived<typename promote<L,R>::type>
operator+ (const base<L, D>& l, const R& r)
{
std::cout << "base + literal" << std::endl;
typedef typename promote<L,R>::type P;
return derived<P>(static_cast<P>(l[0]) + static_cast<P>(r));
}

template <typename L, typename DL, typename R, typename DR>
derived<typename promote<L,R>::type>
operator+ (const base<L, DL>& l, const base<R, DR>& r)
{
std::cout << "base + base" << std::endl;
typedef typename promote<L,R>::type P;
return derived<P>(static_cast<P>(l[0]) + static_cast<P>(r[0]));
}


// self-assigned addition
template <typename L, typename D, typename R>
void
operator+= (base<L, D>& l, const R& r)
{
std::cout << "base += literal" << std::endl;
l[0] += static_cast<L>(r);
return;
}

template <typename L, typename DL, typename R, typename DR>
void
operator+= (base<L, DL>& l, const base<R, DR>& r)
{
std::cout << "base += base" << std::endl;
l[0] += static_cast<L>(r[0]);
return;
}


int main()
{
derived<int> d1(2);
derived<double> d2 = d1 + 3.; // base + literal
derived<double> d3 = d1 + d2; // base + base

d3 += 3.; // base += literal
d2 += d3; // base += base
}
 
V

Victor Bazarov

I am having trouble with overloading the += operator when template
parameters are used. I have a class holding an array (called
"derived" in the following example) which derives from a base class
("base"). I want to be able to add:

1) any derived array holding class to any other derived array holding
class
2) any derived array holding class to a literal value (e.g int,
double, etc) for which addition is defined for the type in the array.

I would like both + and += operators and for appropriate casting to be
handled. A minimal example of what I am trying to do is below (note
that the derived class holds only one value for simplicity).

The two + operators work fine with GCC (versions 3.4 and 4.1). But
the two += don't work when defined together (they work fine when
defined one at a time). When performing derived<L> += derived<R> the
operator+=(base<L,D>, R) is selected rather than
operator+=(base<L,LD>, base<R,RD>). Borland C++ Builder has this
problem and a similar one with the the binary addtion operator. I am
really confused about GCC because the operator prototypes are
practically the same, so it would seem that the the problem += and +
operators are either both correct or wrong.

Using operator+=(base<L,D>, L) works, but without automatic casting I
am after. Any suggestions on how to get around this problem, at least
in GCC would be great!

Example program (88 lines):


#include <iostream>

// curiously recurring base class
template <typename T, typename D>
class base
{
public:
T& operator[](std::size_t i) { return static_cast<D&>(*this); }
const T& operator[](std::size_t i) const { return static_cast<const
D&>(*this); }

};

// curiously recurring derived class
template <typename T>
class derived : public base<T, derived<T> >
{
public:
derived(T t) : t_(t) {}
T& operator[](std::size_t i) {return t_; }
const T& operator[](std::size_t i) const {return t_; }
private:
T t_;
};


// type promotion trait
template <typename L, typename R>
class promote
{
};

template<>
class promote<int, double>
{
public:
typedef double type;
};


// binary addition operators with casting
template <typename L, typename D, typename R>
derived<typename promote<L,R>::type>
operator+ (const base<L, D>& l, const R& r)
{
std::cout << "base + literal" << std::endl;
typedef typename promote<L,R>::type P;
return derived<P>(static_cast<P>(l[0]) + static_cast<P>(r));
}

template <typename L, typename DL, typename R, typename DR>
derived<typename promote<L,R>::type>
operator+ (const base<L, DL>& l, const base<R, DR>& r)
{
std::cout << "base + base" << std::endl;
typedef typename promote<L,R>::type P;
return derived<P>(static_cast<P>(l[0]) + static_cast<P>(r[0]));
}


// self-assigned addition
template <typename L, typename D, typename R>
void
operator+= (base<L, D>& l, const R& r)
{
std::cout << "base += literal" << std::endl;
l[0] += static_cast<L>(r);


This doesn't compile for me when instantiated from

'd2 += d3'

'R' is 'derived<double>', 'L' is 'double'. There is no conversion
from 'derived<double>' to 'double'.

The existence of the other operator+= does not matter here, I am
guessing. The reason is that with 'd3' as the second argument
the compiler cannot deduce 'R' and 'DR' from 'derived said:
return;
}

template <typename L, typename DL, typename R, typename DR>
void
operator+= (base<L, DL>& l, const base<R, DR>& r)
{
std::cout << "base += base" << std::endl;
l[0] += static_cast<L>(r[0]);
return;
}


int main()
{
derived<int> d1(2);
derived<double> d2 = d1 + 3.; // base + literal
derived<double> d3 = d1 + d2; // base + base

d3 += 3.; // base += literal
d2 += d3; // base += base
}

V
 
A

allan.mcrae

After much searching I figured out why this problem is occuring....

If you look at the definition of the addition operators I gave in my
example
template <typename L, typename D, typename R>
derived<typename promote<L,R>::type>
operator+ (const base<L, D>& l, const R& r)
template <typename L, typename DL, typename R, typename DR>
derived<typename promote<L,R>::type>
operator+ (const base<L, DL>& l, const base<R, DR>& r)

When the following commands are compiled
derived<double> d2 = d1 + 3.; // base + literal
derived<double> d3 = d1 + d2; // base + base
however this can't use operator+ (const base<L, D>& l, const R& r) as
the return type is not defined so GCC eliminates it from the overload
resolution and finds operator+ (const base<L, DL>& l, const base<R,
DR>& r) as wanted. Borland C++ Builder doesn't seem to check the
return type.

For the += operators
template <typename L, typename D, typename R>
void
operator+= (base<L, D>& l, const R& r)
template <typename L, typename DL, typename R, typename DR>
void
operator+= (base<L, DL>& l, const base<R, DR>& r)

here the command
d3 += 3.; // base += literal
finds operator+= (base said:
d2 += d3; // base += base
also finds operator+= (base<L, D>& l, const R& r) instead of the wanted
operator+= (base<L, DL>& l, const base<R, DR>& r) as the first is a
"better" match (one less cast) and there isn't the return type problem.


Overloading resolution based on return type definition is entirely new
to me but from what I can find appears to be legal. Learn something
new every day and all...
 

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
473,769
Messages
2,569,582
Members
45,066
Latest member
VytoKetoReviews

Latest Threads

Top