C++ Templates Question

Started by
5 comments, last by Matt_D 14 years, 7 months ago
Hello, It's been a while since I've worked in C++, so I got a little stuck. Here's a simplified version of my problem. I have a templatized class called A which has a data member called "data". I'm trying to overload the * operator (multiplication) for it. I want it to be possible to multiply this class with another instance of the same class. So "A<int> times A<int>" should be possible. But I also want it to be possible to multiple the class with a constant (like an int or a double) instead of a class. So "A<int> times float" should also be possible. All works fine so far when I have just this much defined:

template<class T>
class A
{
	public:

		// Data member
		T data;

		// Constructors

		A<T>()
		{
			this->data = T();
		}

		A<T>(T new_data)
		{
			this->data = new_data;
		}

		// Multiply an instance of A<T> with another instance of A<T>
		A<T> operator*(A<T>& rhs)
		{
			cout << "Performing A<T> times A<T>" << endl;
			return A<T>(this->data * rhs.data);
		}

		// Multiply an instance of A<T> with some other data type
		template<class V>
		A<T> operator*(V c)
		{
			cout << "Performing A<T> times V" << endl;
			return A<T>(this->data * c);
		}

};


Here's some test code:

// Initialize some test numbers
A<int> a1(5);
A<int> a2(7);
double b1 = 4.7;

// Perform the operations
A<int> a3 = a1 * a2;    // Causes to print: "Performing A<T> times A<T>"
A<int> a4 = a1 * b1;    // Causes to print: "Performing A<T> times V"

// Print the results
cout << a3.data << endl;  // Prints 35
cout << a4.data;    // Prints 23 (not 23.5 because a2 is parameterized with "int" instead of "double" but oh well)
But I want to add another layer of complexity, which is where I encounter some problems. I introduce another class B which is derived from A<T>:

template<class T>
class B : public A<T>
{
	public:	
		// Constructor
		B<T>(T new_data)
		{
			this->data = new_data;
		}
};


Now suppose I do this:

A<int> a1(5);
B<int> b1(12);
A<int> a2 = a1 * b1;
cout << a2.data;
The compiler will give an error: Error C2677: binary '*' : no global operator found which takes type 'B<T>' (or there is no acceptable conversion) The error is in the function
template<class V> A<T> operator*(V c)
Apparently, what happens when I try to perform a1 * b1, the compiler picks the wrong operator* function. I want it to pick the first one ("Multiply an instance of A<T> with another instance of A<T>") because B<T> is derived from A<T>. But the compiler picks the second one ("Multiply an instance of A<T> with some other data type"), I guess because B<T> is not exactly A<T>, and the second operator* function is the general catch-all one. What can I do in this case? I want the compiler to pick the first operator* function when I multiple an A<T> type with a B<T> type. (Second bonus question: How might I make it possible to multiple A<int> with A<double>? At the moment, the 'T' parameter has to match when I multiply one instance of A<T> with another instance of A<T>.) EDIT: Here's the complete code, which doesn't compile due to the one error described above:

#include <iostream>
using namespace std;

template<class T>
class A
{
	public:

		// Data member
		T data;

		// Constructors

		A<T>()
		{
			this->data = T();
		}

		A<T>(T new_data)
		{
			this->data = new_data;
		}

		// Multiply an instance of A<T> with another instance of A<T>
		A<T> operator*(A<T>& rhs)
		{
			cout << "Performing A<T> times A<T>" << endl;
			return A<T>(this->data * rhs.data);
		}

		// Multiply an instance of A<T> with some other data type
		template<class V>
		A<T> operator*(V c)
		{
			cout << "Performing A<T> times V" << endl;
			return A<T>(this->data * c);
		}
};


template<class T>
class B : public A<T>
{
	public:	
		// Constructor
		B<T>(T new_data)
		{
			this->data = new_data;
		}
};


int main()
{
	A<int> a1(5);
	B<int> b1(12);

	A<int> a2 = a1 * b1;

	cout << a2.data;

	cin.get();
	return 0;
}

.:<<-v0d[KA]->>:.
Advertisement
1) In the derived class, provide a conversion operator to the baseclass.

2) Have each class provide a conversion operator to the data member type, and the whole problem magically goes away. I think ;¬)
[size="1"]
For the second question, try:
template<typename TT>A<T> operator*(A<TT>& rhs){    return A<T>(this->data * rhs.data);}

Also, some style/performance points. Constructors should use initialization lists, and pass stuff that you don't modify as const reference. Both in the constructors (new_data) and operators (rhs).
Million-to-one chances occur nine times out of ten!
I just made a post on this very question.
You can read it here.
I'm not too hopeful for an elegant solution tho, but in your case I agree with bastard: a conversion operator B<T>::A<T>() is probably the way to go.
It might be helpful to note that inside the class declaration A<T>, you can refer to A<T> by just A:

template <typename T> struct A {    A operator * (A const & rhs) const {...}};


Also note that you can make a non-member version by declaring friend functions inside the class declaration:
template <typename T> struct A {    friend A operator * (A const & lhs, A const & rhs) {...}};


The function operator*(A const&, A const&) will then be in the namespace of A.

Of course you can also write a template friend function:
template <typename T> struct A {    template <typename U>    friend A operator * (A const & lhs, A<U> const & rhs) {...}};
Thanks for all the help!

I got this almost solved. It leaves only a little bit to be left desired.

I implemented the conversion operator in B<T>, so class B looks like this now:

template<class T>class B : public A<T>{	public:			// Constructor		B<T>(T new_data)		{			this->data = new_data;		}		// Conversion operator		operator A<T> () const		{			return A<T>(this->data);		}};


Then it took some brain racking to figure this next one out. Turns out it's necessary to change the second operator* function in A from this:

template<class V> A<T> operator*(V c)

To this:

template<class V> A<T> operator*(V& c)

Otherwise, the compiler would still keep picking the second (generic) operator* function when I multiply A<T> with B<T>, even when I use the conversion operator explicitly. I recall that two member functions can differ only by a reference, which seems kind of silly and useless to me, but it turned out to be part of the problem in my case.

But here's the part that leaves a little bit to be left desired. It looks like I'm still required to explicitly cast b1 to type A<T> using the conversion operator. It doesn't seem to happen implicitly. So I still can't really say:

A<int> a2 = a1 * b1;

I have to say:

A<int> a2 = a1 * (A<int>)b1;

Not particularly elegant, but I guess as good as it gets?

Anyways, thanks again to all. I will be implementing the style/notation suggestions next.
.:<<-v0d[KA]->>:.
this is one of those things that makes great "and this is why we dont recommend overloading arithmetic operators in c++" posts :)

so, part of why the compiler doesnt know how to convert from an B to an A, is that both types are templated.
B<float> is completely different to A<int>. You might get the compiler to play nice if both a1 and b1 are derived from the same base templated type (eg: A<int> and B<int>).

the other thing that worries me is that you could easily be slicing, as a1, and b1 are not (in this simplified example) pointers. your possibly casting away valuable information.

the real question is, what problem are you actually trying to solve with all this? a better understanding of what your trying to achieve will help us give you better answers :)
your never as good as they say you were, never as bad as they say you was.

This topic is closed to new replies.

Advertisement