I have a template class with an overloaded + operator. This is working fine when I am adding two ints or two doubles. How do I get it to add and int and a double and return the double?
template <class T>
class TemplateTest
{
private:
T x;
public:
TemplateTest<T> operator+(const TemplateTest<T>& t1)const
{
return TemplateTest<T>(x + t1.x);
}
}
in my main function i have
void main()
{
TemplateTest intTt1 = TemplateTest<int>(2);
TemplateTest intTt2 = TemplateTest<int>(4);
TemplateTest doubleTt1 = TemplateTest<double>(2.1d);
TemplateTest doubleTt2 = TemplateTest<double>(2.5d);
std::cout << intTt1 + intTt2 << /n;
std::cout << doubleTt1 + doubleTt2 << /n;
}
I want to be able to also do this
std::cout << doubleTt1 + intTt2 << /n;
Stephen has already given a good explanation of the problems you may encounter with this. You can define overloads for all the possible combinations of all the instantiations of the template (so, you'd effectively have operators defined for double + double, int + double, double + int, etc.). This can get unwieldy fast and can be difficult to keep track of which combinations are supported.
You might be better off using a non-member function named something like Add()
. The advantage of doing this is that you can specify the return type. The disadvantage is that you have to specify the return type. :-) In general, though, this is better than performing unexpected conversions automatically.
template <typename R, typename T, typename U>
TemplateTest<R> Add(const TemplateTest<T>& t, const TemplateTest<U>& u)
{
return TemplateTest<R>(t.x + u.x);
}
invoked as:
std::cout << Add<double>(intTt1, doubleTt1) << std::endl;
C++0x will add support for a number of language features that will make this much simpler and will allow you to write a reasonable operator+
overload:
template <typename T, typename U>
auto operator+(const TemplateTest<T>& t, const TemplateTest<U>& u)
-> TemplateTest<decltype(t.x + u.x)>
{
return TemplateTest<decltype(t.x + u.x)>(t.x + u.x);
}
This will ensure that the usual arithmetic conversions (integer promotion, conversion to floating point, etc.) are performed and you end up with the expected result type.
Your C++ implementation may support these C++0x features already; you'd want to consult the documentation of whatever compiler you are using.
Here be dragons. You're getting into parts of c++ that will probably result in a lot of questions posted to StackOverflow :) Think long and hard about if you really want to do this.
Start with the easy part, you want to allow operator+
to add types that are not always the same as T
. Start with this:
template <typename T2>
TemplateTest<T> operator+(const TemplateTest<T2>& rhs) {
return TemplateTest<T>(this->x + rhs.x);
}
Note that this is templated on T2
as well as T
. When adding doubleTt1 + intTt2
, T
will be doubleTt1
and T2
will be intTt2
.
But here's the big problem with this whole approach.
Now, when you add a double
and an int
, what do you expect? 4 + 2.3 = 6.3
? or 4 + 2.3 = 6
? Who would expect 6
? Your users should, because you're casting the double back to an int
, thus losing the fractional part. Sometimes. Depending on which operand is first. If the user wrote 2.3 + 4
, they would get (as expected?) 6.3
. Confusing libraries make for sad users. How best to deal with that? I don't know.
I want to be able to also do this
std::cout << doubleTt1 + intTt2 << "\n";
What you'll probably need for this case are type traits. Basically, those are template classes containing typedef
s. You then partially specialize such a template to override the typedef
s.
(This is probably a bit naïve, but it should get the basic idea across.)
template <typename A, typename B>
struct add_traits
{
typedef A first_summand_t; // <-- (kind of an "identity type")
typedef B second_summand_t; // <-- (ditto; both aren't strictly necessary)
typedef B sum_t; // <-- this is the interesting one!
};
Now you partially specialize that thing for various combinations of A
and B
:
template<>
struct add_traits<int, int>
{
typedef int first_summand_t;
typedef int second_summand_t;
typedef int sum_t; // <-- overrides the general typedef
};
template<>
struct add_traits<int, double>
{
typedef int first_summand_t;
typedef double second_summand_t;
typedef double sum_t; // <-- overrides the general typedef
};
template<>
struct add_traits<double, int>
{
typedef double first_summand_t;
typedef int second_summand_t;
typedef double sum_t; // <-- overrides the general typedef
};
Now you could write a fairly generic add operation that went like this:
template <typename A, typename B>
typename add_traits<A,B>::sum_t add(A first_summand, B second_summand)
{
// ...
}
As you can see, you don't specify a concrete return type; instead, you let the compiler figure it out through the add_traits
template class. Once the compiler generates the code for a particular add
function, it will look up the types in the corresponding add_traits
class, and thanks to the partially specialized versions that you provided, you can make sure that certain type "combinations" will be applied.
P.S.: The same technique would e.g. also be useful when you want to subtract unsigned numbers. One unsigned int
subtracted from another can result in a negative answer; the result type would have to be a (signed
) int
.
P.P.S.: Corrections made according to the comments below.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With