Removing ambiguity when using sqrt() function in a template class
Pregunta
I have a complex number class where I am trying to implement a function to work out the modulus (which requires the used of a sqrt).
My header file is as follows:
#ifndef MY_CLASS_H
#define MY_CLASS_H
template <class T> class complex
{
// function to overload operator<< a friend
template <class T>
friend std::ostream& operator<< (std::ostream &os, const complex<T> &z);
private:
T re,im;
public:
// Constructors & destructor
complex(){re=im=0;}
complex( const T& r, const T& i ) : re(r), im(i) {}
~complex(){}
// Return real component
T realcomp() const {return re;}
// Return imaginary component
T imagcomp() const {return im;}
// Return modulus
double modulus() {return sqrt(im*im + re*re);}
etc....
The compiler outputs the error:
error C2668: 'sqrt' : ambiguous call to overloaded function
could be 'long double sqrt(long double)'
or 'float sqrt(float)'
or 'double sqrt(double)'
which I know is telling me that the sqrt
needs to know what type of data it is passing through it.
For my program, im
and re
will take either double
or int
values.
Am I right in saying sqrt will only take floating values? If so how do I force im and re to floating points without a 'loss of data' warning. Can I do this without converting them?
Solución
No, sqrt generally does not take floats. What it takes depends a on your libraries. In your case, you have several overloaded sqrt functions, one takes float
, one takes double
, and one takes long double
.
The problem you have, is none of them take int
, and there are multiple conversions from int to a type that you could be used, so the compiler makes you choose (by casting). If you are only using double
or int
, then cast to double
-- there will be no loss of presision. If you want to use long double
at some point, cast to that.
double modulus() {return sqrt((double)im*im + re*re);}
Otros consejos
There are several solutions, but first, there is a problem with your
code: where does the function sqrt
come from. If the user includes
<sqrt.h>
, then you should get only the double
version, and no
ambiguities. If the user includes <csqrt>
, then in pre C++11, the
code shouldn't find any sqrt
; in practice, no compiler implemented
this correctly, and what you get depends on the implementation.
The safest solution is to declare a special namespace of your own,
include <csqrt>
, define the sqrt
you need in it, using std::sqrt
in their implementation, and call the sqrt
in your namespace:
#include <csqrt>
namespace SafetyFirst
{
inline int
sqrt( int in )
{
return static_cast<int>( std::sqrt( static_cast<double>( in ) ) );
}
inline double
sqrt( double in )
{
return std::sqrt( in ) ;
}
// And so on for any other types you might need. The
// standard provides std::sqrt for the floating point
// types only.
}
This way, overload resolution will always find an exact match, and you
determine exactly which function you actually want. And you have a way
for clients to define new numeric types which might be usable: they just
have to define their sqrt
in the same namespace, probably forwarding
to an implementation in the same namespace as the type.
Alternatively, you can do:
#include <cmath> // To ensure getting a fixed set of overloads
using std::sqrt;
inline int
sqrt( int in )
{
return static_cast<int>( std::sqrt( static_cast<double>( in ) ) );
}
// And so on for any standard integral types you want...
// And your class here...
For client defined types, ADL will ensure that the compiler looks in the correct namespace, so they don't have to provide a forwarding function in your namespace.
This is actually a fairly nice solution, except that it could screw up
client code not expecting to find std::sqrt( float )
in the global
namespace. (Such code isn't portable, but it could exist on some
platforms.)
Try this:
double modulus() {return sqrt((double)im*im + re*re);}
You'll always invoke double sqrt(double)
this way, but that's probably ok, given your description.
The problem is caused by ambigues input of the function call of sqrt. The sqrt function has different input parameter (overloaded function) either float or double.
I have quite similar problem : in the first code I wrote :
#include <iostream> // std::cout
#include <cmath> // std::sqrt
int main()
{
float ss;
ss= std::sqrt(120); //in this line compiler not sure 120 is float or double
std::cout<<ss;
std::cin>>ss;
return 0;
}
in this code, compiler not sure the parameter is float or double
we can just fix the code just to make sure which type of the parameter is, either float or double as follow :
#include <iostream> // std::cout
#include <cmath> // std::sqrt
int main()
{
float ss; //you can change to double
ss=120; //this line make compiler sure 120 is float
ss=std::sqrt(ss); //in this line compiler sure that sqrt function
std::cin>>ss;
return 0;
}
Maybe a bit late here, but I got an help from templates to help the compiler chose the right function.
#ifndef SQUARE_ROOT_HPP
#define SQUARE_ROOT_HPP
#include <cmath>
template<typename Scalar>
Scalar square_root(Scalar value)
{
return square_root(static_cast<double>(value));
}
template<>
float square_root(float value)
{
return std::sqrt(value);
}
template<>
double square_root(double value)
{
return std::sqrt(value);
}
template<>
long double square_root(long double value)
{
return std::sqrt(value);
}
#endif // SQUARE_ROOT_HPP
My problem was similar: I'm expanding my old source to make it fit for an additional use of mpreal - an annoying job. One of the last problems: a little statement in a big template:
`T help = sqrt(g.eval(f.eval(a))());`
I had to struggle .. whatever I tried was failing - very different error messages, some looking crazy, like illegal conversion from complex sqrt, but I used only float types; converting mpreal to long double throwing away precision.
if(std::is_floating_point<T>::value) ..
and if(std::is_same<T,long double>::value) ..
were giving misleading error messages, especially when T was long double,
using std:: and mpfr:: in then- respective else-part was leading to not resolvable type errors in then- or else-part depending on T.
I would have found a solution here - if I only had known the source of my problem ...
My solution:
inline long double wurz(long double x) { return std::sqrt(x); };
inline mpreal wurz(mpreal x) { return sqrt(x); };
template<class T>
....
real_word helpa2 = wurz(g.eval(f.eval(a))());
I found this page here when I wanted to ask, if there's a more natural, less stupid solution than mine ..