namespace math {
template<typename T> inline T sqrt (T value) {
return (::sqrt (value));
}
}
int main (...) {
float foo = math::sqrt (4.0f); // does work
long bar = math::sqrt (4); // does not work, the compiler reports: Error 2 error C2668: 'sqrt' : ambiguous call to overloaded function
}
template question
Heya,
i've got a question about templates. Lately i played around with templates and tried something like:
Which makes sense, because the orginal sqrt() function does only take, "long double", "double", "float". So far so good. Now the real question. *g*
Is there are a way to detect, if the type T needs to be typcasted and if so, build the function with the required typecast ?
Or does this only works with explicit specialization ?
Please bear with me and the example, i'm not so into templates ;)
Thanks in advance!
Impz0r
I believe that the most effective way to do this would be via template specializations. Which is also the exact mechanism for accomplishing what you want to do.
For something simple like you example I agree with daerid. for something more complex you could use std::type_info and typeid(T)
the only way to do what you want is manually, because their could never be a case of having the compiler choose automatically which conversion to use in the situation in which the problem is that it doesn't know which version to use.
Looking at what you are doing, there are 2 distinct pieces:
1. You are wrapping the stand-alone overloaded sqrt() functions inside namespace math. You choose to do this using a templated function which should work perfectly well.
2. You want to provide additional overloads which allow the user to pass types other than the 2 supported by the source functions.
The problem with 2 is ... WHICH overloads, if you do provide such a function you are actually hiding the conversion from the client which uses it, and thereby likely making them unaware that there was even a choice.
It would be difficult to determine which implementation is better for:
int val;
...
int result = math::sqrt(val);
should you use float for efficiency, double for accuracy, or long double for reasons largely tied to a particular platform.
What you should do depends on your purpose ... if writing a general math wrapper library you should likely not add overloads ... if you are writing a library for your specific need, then you should simply pick one version (float, double, etc) and use it for integral type up-conversions.
Looking at what you are doing, there are 2 distinct pieces:
1. You are wrapping the stand-alone overloaded sqrt() functions inside namespace math. You choose to do this using a templated function which should work perfectly well.
2. You want to provide additional overloads which allow the user to pass types other than the 2 supported by the source functions.
The problem with 2 is ... WHICH overloads, if you do provide such a function you are actually hiding the conversion from the client which uses it, and thereby likely making them unaware that there was even a choice.
It would be difficult to determine which implementation is better for:
int val;
...
int result = math::sqrt(val);
should you use float for efficiency, double for accuracy, or long double for reasons largely tied to a particular platform.
What you should do depends on your purpose ... if writing a general math wrapper library you should likely not add overloads ... if you are writing a library for your specific need, then you should simply pick one version (float, double, etc) and use it for integral type up-conversions.
Thanks daerid!
So that means, i define for every type another template function like:
Hmm doesn't that kinda defeat the purpose of a template ?
@Anonymous Poster
How exactely would look something like this ?
@Xai
First off, thanks for your replay!
So in your second point you mean, let the user typecast like:
Well that would work i guess and is most likely the "cleanest" solution?
Impz0r
So that means, i define for every type another template function like:
namespace math { template<typename T> inline T sqrt (T value) { return (::sqrt (value)); } template<> inline long sqrt (long value) { return (static_cast<long>(::sqrt (static_cast<float> (value)))); } ... etc ...}
Hmm doesn't that kinda defeat the purpose of a template ?
@Anonymous Poster
How exactely would look something like this ?
@Xai
First off, thanks for your replay!
So in your second point you mean, let the user typecast like:
namespace math { template<typename T> inline T sqrt (T value) { return (::sqrt (value)); } float foo = math::sqrt (4.0f); long bar = static_cast<long> (math::sqrt (static_cast<float> (4))); // something like this ?}
Well that would work i guess and is most likely the "cleanest" solution?
Impz0r
You can do it with boost::enable_if and Boost.TypeTraits:
There might be a more elegant way by importing std::sqrt into the math namespace. If that doesn't work, you can also use enable_if as a final default parameter (I like this version better, though).
jfl.
#include <cmath>#include <boost/type_traits.hpp>#include <boost/utility/enable_if.hpp>namespace math { template< class T > typename boost::disable_if< boost::is_floating_point< T >, T >::type sqrt( T value ) { return static_cast< T >( std::sqrt( static_cast< double >( value ) ) ); } template< class T > typename boost::enable_if< boost::is_floating_point< T >, T >::type sqrt( T value ) { return std::sqrt( value ); }}int main() { float foo = math::sqrt( 4.0f ); long bar = math::sqrt( 4 );}
There might be a more elegant way by importing std::sqrt into the math namespace. If that doesn't work, you can also use enable_if as a final default parameter (I like this version better, though).
jfl.
Quote:Original post by Impz0r
Thanks daerid!
So that means, i define for every type another template function like:
*** Source Snippet Removed ***
Hmm doesn't that kinda defeat the purpose of a template ?
Yep. One way to modify only part of the code for each type, but still have a generic function is to use traits.
template<typename T> class CastType;template<> class CastType<int> {public:typedef float Type;};... etc ...template<typename T> inline T sqrt (T value) { return (static_cast<CastType<T>::Type>(::sqrt (static_cast<float> (value))));}
(Might need a typename in there when referencing "Type", I always forget those.)
Basicly this lets you replace part of your generic function with specific code for each type (in this case the cast), while keeping the rest of the code generic.
Thanks jflanglois!
Unfortunately i'm not quite sure if i follow your solution. I haven't used those boost templates till now. Does boost check the typeid behind the scene to check what kind of a type it was given ? Those checks aren't executed at runtime are they ?
And what did you mean with:
Impz0r
Unfortunately i'm not quite sure if i follow your solution. I haven't used those boost templates till now. Does boost check the typeid behind the scene to check what kind of a type it was given ? Those checks aren't executed at runtime are they ?
And what did you mean with:
Quote:?
"There might be a more elegant way by importing std::sqrt into the math namespace"
Impz0r
Quote:Original post by Impz0r
Thanks jflanglois!
Unfortunately i'm not quite sure if i follow your solution. I haven't used those boost templates till now. Does boost check the typeid behind the scene to check what kind of a type it was given ? Those checks aren't executed at runtime are they ?
No, it's all at compile time. The typetraits look like what Solias does with his CastType class: for instance, is_integral< int > is a spcialization of is_integral< T > where is_integral::value is true.
Quote:And what did you mean with:Quote:?
"There might be a more elegant way by importing std::sqrt into the math namespace"
I meant putting a using std::sqrt; as a default instead of implementing math::sqrt in terms of std::sqrt.
[edit: Um, stupid me. I meant this:
namespace math { template< class T > typename boost::disable_if< boost::is_floating_point< T >, T >::type sqrt( T value ) { return static_cast< T >( std::sqrt( static_cast< double >( value ) ) ); } using std::sqrt;}int main() { float foo = math::sqrt( 4.0f ); long bar = math::sqrt( 4 );}
Thanks Solias & jflanglois!
So to get it right, the solutions of you both are quite similar? Except i don't have to do much, if i'm using boost ? :p
One question about your solution jflanglois. If i read it correctly, all types which are not floats going through the first function and the rest through the second one ? Does this mean, i don't have to implement a bunch of explicit template functions for all kind of types then ? Only the two and i'm done ?
[Edit]
About the "using std::sqrt", i'm not quite sure what it does there. Could you please explain?
Impz0r
So to get it right, the solutions of you both are quite similar? Except i don't have to do much, if i'm using boost ? :p
One question about your solution jflanglois. If i read it correctly, all types which are not floats going through the first function and the rest through the second one ? Does this mean, i don't have to implement a bunch of explicit template functions for all kind of types then ? Only the two and i'm done ?
[Edit]
About the "using std::sqrt", i'm not quite sure what it does there. Could you please explain?
Impz0r
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement