Reputation: 3054
I got a wrapper class that has a simple and light-weighted implicit conversion operator to double
.
I like to use it as I would use a double, for example:
if (!std::isfinite(myVar)) ...
But visual c++ implementation of std::isfinite(double)
is actually a template that get his argument by copy.
So my wrapper class copy constructor is called, and it is not light-weighted.
To avoid this, I have to write:
if (!std::isfinite((double)myVar)) ...
for every call :(
If visual c++ std::isfinite()
was defined as is it on cppreference.com, I would not have to cast every call: ([edit] I may be wrong, Integral isn't an actual type... but... [edit] It still should not accept user defined types ?)
bool isfinite( float arg );
bool isfinite( double arg );
bool isfinite( long double arg );
bool isfinite( Integral arg );
I am not sure what the standard says about this.
Is vc++ template std::isfinite
standard-conforming ?
Should I report this as a bug on Microsoft connect ?
Should I define my own isfinite(double)
that calls std::isfinite
?
Or maybe it is a non-issue as in a release build the calls get inlined and no copy occurs ? (well I'll try to check it right now and update in a few minutes)
It doesn't seem to get inlined in a release build with /Ob2 (inline any suitable function)
as requested, a sample:
struct DoubleWrapper {
double value;
DoubleWrapper(double value) : value(value) {
printf("copy Ctor\n");
}
DoubleWrapper(const DoubleWrapper & that) : value(that.value) {}
operator double() const {
return this->value;
}
};
int main() {
DoubleWrapper a(rand()); //rand to prevent optimization
auto res = std::isfinite(a);
printf("%d", res); //printf to prevent optimization
}
So, based on Ben Voigt comment, this is what I added to my class header:
#include <cmath>
namespace std {
inline bool isfinite(const DoubleWrapper<double> & dw) {
return isfinite((double)dw);
}
}
Is this a correct solution ?
The only thing is, should I do the same for all <cmath>
functions that takes a double ?
I'm responding to Shafik Yaghmour answer here, because a comment is too limited (maybe I should start a new question)
If I understand correctly, instead of my edit 4, I should add this to my class header:
inline bool isfinite(const DoubleWrapper<double> & dw) {
return isfinite((double)dw);
}
using std::isfinite;
Is it required to put it in a namespace, or can I leave it in the "global" namespace ?
But this mean I have to change all my calls from std::isfinite(dw)
to isfinite(dw)
.
Ok, but I realize there are many things I don't understand. I am confused.
I understand that adding an overload to std is not allowed.
However, adding a template specialization is allowed ? Why ? What difference does it makes ?
Anyway, I tried it, and it is not a solution to my problem because this specialization:
template<> inline __nothrow bool std::isfinite(const MagicTarget<double> & mt) {
return std::isfinite((double)mt);
}
will not be selected by the compiler over the standard one:
template<class _Ty> inline __nothrow bool isfinite(_Ty _X)
{
return (fpclassify(_X) <= 0);
}
To be selected, it should be
template<> inline __nothrow bool std::isfinite(MagicTarget<double> mt) {
return std::isfinite((double)mt);
}
But this would still call the copy Ctor :(
Surprisingly the overload (see edit 4) is selected over the standard template... I am beginning to think that some C++ rules are too subtle for me :(
But, to begin with, why on earth are cmath functions and especially std::isfinite
a template ?
What's the point of accepting anything else that floating points types ?
Anyway, vc++ std::isfinite
calls std::fpclassify
that is only defined for float, double and long double.
So... What's the point?
I am thinking the standard committee screwed up by allowing cmath functions to be templates. They should only be defined for the types that are relevant, or maybe takes their arguments as universal references.
That's it, sorry for the rant...
I will go for the (not in std) overload. Thank you!
Upvotes: 4
Views: 1798
Reputation: 158589
Addressing edit 4 to your question since through the comments you have come close to a final solution.
You should not be adding it to the std
namespace, you should add it to one of your own namespace and rely on argument dependent lookup see Is it a good practice to overload math functions in namespace std in c++ for more details.
Given the standard committee's apparent desire to restrict cmath
functions to be called with arithmetic types only, relying on an implicit conversion is not a good idea. So performing an explicit conversion via a cast is the safe way to go for all cmath
functions:
isfinite((double)dw)
You can find the details of this in Is it valid to pass non-arithmetic types as arguments to cmath functions?.
Upvotes: 3