tags:

views:

1096

answers:

8

Hi, I'm a little lost in how to cast templates. I have a function foo which takes a parameter of type ParamVector<double>*. I would like to pass in a ParamVector<float>*, and I can't figure out how to overload the casting operator for my ParamVector class, and Google isn't helping me that much. Does anyone have an example of how to do this? Thanks.

EDIT: Adding some code, sorry I'm an idiot and didn't phrase the original question well at all;

template<class T> class ParamVector 
{
public:
 vector <T> gnome;
 vector <T> data_params;
}

template<class T> class ParamVectorConsumer
{
 public:
 ParamVector<T> test;
}

ParamVector<float> tester;
ParamVectorConsumer<double> cons;
cons.ParamVector = tester

will fail to compile. I would like to know how to write it so that I can cast the float version of tester to a ParamVector double. Thanks

EDIT2: Casting was the wrong word. I don't mind writing extra code, I just need to know how to get this to be accepted by the compiler so that I can write some sort of conversion code.

+2  A: 

You are lost because you can't do it - the two types are completely different. Whenever you come across the need for a cast in your code, you should examine both your code and your design very closely - one or both is probably wrong.

anon
+1  A: 

You can't do this with a direct cast because double and float are completly different sizes. Doubles are going to be 64 bits while floats are 32. A pointer forced to cast from a

ParamVector<float>

to

ParamVector<double>

is going to misinterpret the data and give you garbage. You may want to google "pointer aliasing" or just learn more about pointers in general to see how this isn't going to work.

Think about it for a second you have one array that is a bunch of 64 bit values with fields layed out like this

0 => abcdabcd12341234
1 => abcdabcd12341234

If you force this to be interpreted as an array of 32 bit values, its going to not be interpreted correctly. You may or may not get something like

0 => abcdabcd
1 => 12341234
2 => abcdabcd
3 => abcdabcd

or it could be switched so that the 12341234's come first, or something stranger due to how the word ordering works out.

Doug T.
I thought that would be implicit casting and C++ wouldnt complain about that? C# would but not C++ right?
TStamper
C++ accepts float and double as implicit casts, but cannot accept pointer-to-float-array and pointer-to-double-array as implicit casts. floats and doubles are values, float* and double* are memory objects. Since sizeof(float) may differ from sizeof(double), float* cannot be accepted as double*.
Thomas L Holaday
+2  A: 

C++ FAQ has a section that explains that very well!

Hope it helps!

+5  A: 

I'm not sure but maybe you need some like this:

template< typename TypeT >
struct ParamVector
{
    template < typename NewTypeT >
    operator ParamVector< NewTypeT >()
    {
     ParamVector< NewTypeT > result;
     // do some converion things
     return result;
    }

    template< typename NewTypeT >
    ParamVector( const ParamVector< NewTypeT > &rhs )
    {
     // convert
    }

    template < typename NewTypeT >
    ParamVector& operator=( const ParamVector< NewTypeT > &rhs )
    {
     // do some conversion thigns
     return *this;
    }


};
ParamVector< double > d1;
ParamVector< float > f1;
f1 = d1;

You can choose use conversion operator or operator= - I've provided both in my example.

bb
Nice. I've edited and added additional info in example.
bb
I'm sorry, but this is really terrible advice. This has the worst possible memory characteristics. I question the OP's need to have his foo function take d ParamVector<float>. Make the foo function templatized so it can take any ParamVector<T>.
Matt Cruikshank
I don't like implicit conversion too. But it is answer on asked question. What same is terrible by your opinion?
bb
BTW: same casting we will have for usual variables in double = float, int = double.. etc. Compiller warnings and errors will notify us about all possible problems.
bb
+1  A: 

You mentioned "template casting" in your headline, so I'll presume that ParamVector is a templated type. That means that foo could be templated as well, and that would solve your problem.

template <typename T>
void foo(ParamVector<T> const& data)
{
}
Matt Cruikshank
+1  A: 

You can't cast templates like this because the types are unrelated.

However, you can add a conversion function, such as:

(Your code wasn't really complete, so I can post complete code either. Hopefully you will get the idea.)

template<class T> class ParamVectorConsumer
{
 public:
 ParamVector<T> test;

 template<T2> ParamVectorConsumer<T2> convert()
 {
   ParamVectorConsumer<T2> ret;
   ret = this->...
}
John Dibling
+2  A: 

Well, you can't. Each different actual template parameter, makes an entirely new class, which has no* relation inheritance relation with any any other class, with a diffent actual argument, made from that template.

No relationship. Well, except that each provides the same interface, so that inside a template you can handle then the same.

But neither the static types or the dynamic types have any relation.

Let me drop back here, and explain.

When I declare a pointer to classtype, like

Foo fp*;

fp has what we call a static type, of pointer-to Foo. If class Bar is a subclass of Foo, and I point fp at new Bar:

fp = new Bar1();

then we say that the object pointed to by fp has the dynamic type of Bar.

if Bar2 also publicly derives from Foo, I can do this:

fp = new Bar2();

and without ever even knowing what fp points to, I can call virtual methods declared in Foo, and have the compiler make sure that the method defined in he dynamic type pointed to is what's called.

For a template< typename T > struct Baz { void doSomething(); };

Baz<int> and Baz<float> are two entirely different class types, with no relationship.

The only "relationship" is that I can call doSomething() on both, but since the static types have no relationship, if I have a Baz<int> bi*, I can't point it to a Baz<float>. Not even with a cast. The compiler has no way to "translate" a call to the Baz doSotheing method into a call to a Baz::doSomething() method. That's because there is no "Baz method", there is no Baz, there are ony Baz<int>s and Baz<float>s, and Baz<whatevers>, but there's no common parent. Baz is not a class, Baz is a template, a set of instructions about how to make a class if and only if we have a T parameter that's bound to an actual type (or to a constant).

Now there is one way I can treat those Bazes alike: in a template, they present the same interface, and the compiler, if it knows what kind of Baz we're really dealing with, can make a static call to that method (or a static access of a member variable).

But a template is not code, a template is meta-code, the instructions of how to synthesize a class. A "call" in a template is not a call,it's an instruction of how to write the code to make a call.

So. That was long winded and confusing. Outside of a template definition, there is no relationship between a ParamVector and aParamVector. So your assignment can't work.

Well. Almost.

Actually, with partial application of templates, you can write a template function which gives a "recipe" of how to transform a Paramvector<T> to a ParamVector<U>. Notice the T and the U. If you can write code to turn any kind of ParamVector, regardless of actual template parameter into any other kind of ParamVector, you can package that up as a partially applied template, and the compiler will add that function to, for example, ParamVector.

That probably involves making a ParamVector<U>, and transforming each T in the ParamVector<T> into a U to put in the ParamVector<U>. Which still won't let you asign to a ParamConsumer<T>.

So maybe you want to have both templates and inheritance. In that case, you can same that all ParamVectors regardless of type inherit from some non-template class. And then there would be a relationship between ParamVectors, they'd all be sibling subclasses of that base class.

tpdi
+2  A: 

Notice that when you do an implicit cast, what the compiler can do without your help (I mean, without additional code) is just reference-upcast. That means that, seeing the object as a reference (for cast purposes only, the nature of the object doesn't change of course), it can look at it as one of its ancestors. When you have two template instances, none of them is an ancestor of the other (neither they are necessarily in the same hierarchy). After trying that, the compiler looks for cast operators, constructors, etc. At this stage, probably a temporary object needs to be created, except when you're doing attribution and there's an attribution operator that fits.

One solution to your problem would be to use a conversion constructor:

template<class T> class ParamVector 
{
public:
    vector <T> gnome;
    vector <T> data_params;

    ParamVector()
    {
    }
    template <class T2> ParamVector(const ParamVector<T2> &source)
    {
     gnome.reserve(source.gnome.size());
     copy(source.gnome.begin(), source.gnome.end(), gnome.begin());
     data_params.reserve(source.data_params.size());
     copy(source.data_params.begin(), source.data_params.end(), data_params.begin());
    }
};

This would create a temporary object whenever you use an instance of the template and other is required. Not a good solution if you're dealing with large containers, the overhead isn't acceptable. Also, if you pass a template instance to a function that requires not an object but a reference, the compiler won't call the conversion constructor automatically (you have to do an explicit call).

Fabio Ceconello