views:

1269

answers:

5

Assume I have a function template like this:

template<class T>
inline
void
doStuff(T* arr)
{
  // stuff that needs to use sizeof(T)
}

Then in another .h filee I have a template class Foo that has:

public: operator T*() const;

Now, I realize that those are different Ts. But If I have a variable Foo<Bar> f on the stack, the only way to coerce it to any kind of pointer would be to invoke operator T*(). Yet, if call doStuff(f), GCC complains that doStuff can't take Foo<Bar> instead of automatically using operator T*() to coerce to Bar* and then specializing the function template with Bar as T.

Is there anything I can do to make this work with two templates? Or does either the argument of the template function have to be a real pointer type or the template class with the coercion operator be passed to a non-template function?

A: 
JaredPar
Dereferencing a reference is in general bad style. I know hacks have their value, but only used extremely sparingly.
Paul de Vrieze
Leon Timmermans
A: 

Well, T* is not a separate type from T in the sense you think it is. The pointer is a type qualifier. I'm not sure what the standard says about it, but I would say that as the variable is already of type T it does not try to convert again. If you want to do some custom stuff to get a pointer, overload the & operator.

Paul de Vrieze
T* is a separate type from T in the sense everybody thinks it is...
Leon Timmermans
+3  A: 

GCC is correct. In template arguments only exact matches are considered, type conversions are not. This is because otherwise an infinite (or at least exponential) amount of conversions could have to be considered.

If Foo<T> is the only other template that you're going to run in to, the best solution would be to add:

template<typename T> inline void doStuff(const Foo<T>& arr) {
    doStuff(static_cast<T*>(arr));
}

If you are having this issue with a lot of templates, this one should fix it:

#include <boost/type_traits/is_convertible.hpp>
#include <boost/utility/enable_if.hpp>
template<template <typename> class T, typename U> inline typename boost::enable_if<typename boost::is_convertible<T<U>, U*>::type>::type doStuff(const T<U>& arr) {
    doStuff(static_cast<U*>(arr));
}

It's a bit verbose though ;-)

Leon Timmermans
+1  A: 

This might be worth a try:

doStuff<Bar>(f);

I think this will cause the compiler to expect T* to be Bar* and then use Foo's operator T*() to perform the cast, but I can't say I've tried it.

It is correct. The compiler is complaining because Template Argument Deduction fails. Once you provide the argument, there is no TAD. At that point, the compiler knows both the target and destination type for the conversion sequence.
MSalters
A: 

Leon's idea is probably best. But in a pinch, you could also call the cast operator explicitly:

doStuff(static_cast<Bar*>(f));
Eclipse