Reputation: 920
Assume we have a global object pi
and we want to implicitly convert it to either float
or double
depending on the context. The following does not work:
#include <cmath>
class Pi {
public:
Pi() {}
operator float() const {
return std::atan(1.0f)*4.0f;
}
operator double() const {
return std::atan(1.0)*4.0;
}
};
const Pi pi;
#include <iostream>
#include <iomanip>
int main() {
std::cout << std::setprecision(50) << pi * 1.0f << std::endl;
std::cout << std::setprecision(50) << pi * 1.0 << std::endl;
}
The reason why it does not work is that the compiler does not know whether it should implicitly convert pi
to a float
or a double
. However, say we always want it to convert to the type of the other operand in a binary arithmetic operator. Is there some elegant way to achieve that in C++11 or later or should I overload all arithmetic operators? Something like that:
class Pi {
public:
Pi() {}
float operator*(float x) const {
return (std::atan(1.0f)*4.0f) * x;
}
double operator*(double x) const {
return (std::atan(1.0)*4.0) * x;
}
//...
};
Or can you come up with a more elegant solution for implicitly converting the global object to an appropriate type for the context?
Upvotes: 4
Views: 158
Reputation: 302708
Honestly, I would just stick with the path of least surprise:
class Pi {
...
explicit operator float() { ... }
explicit operator double() { ... }
...
};
And just cast it when you want it:
std::cout << std::setprecision(50) << (float)pi * 1.0f << std::endl;
This is just a precursor for variable templates, in which you would want just write pi<float>
. It's the same number of characters either way.
Upvotes: 2
Reputation: 41092
Your first try is close. Let's add a templated *
operator outside the class:
template<typename T>
T operator*(const Pi& pi, T&& other)
{
return static_cast<T>(pi) * std::forward<T>(other);
}
And now it works. Live Demo
#include <cmath>
#include <iostream>
#include <iomanip>
class Pi {
public:
Pi() {}
operator float() const {
return std::atan(1.0f)*4.0f;
}
operator double() const {
return std::atan(1.0)*4.0;
}
};
const Pi pi;
template<typename T>
T operator*(const Pi& pi, T&& other)
{
return static_cast<T>(pi) * std::forward<T>(other);
}
int main() {
std::cout << std::setprecision(50) << pi * 1.0f << std::endl;
std::cout << std::setprecision(50) << pi * 1.0 << std::endl;
}
Output:
3.1415927410125732421875
3.141592653589793115997963468544185161590576171875
If you want to be really strict about what substitutes for T
look into std::enable_if
and std::is_floating_point
within type_traits
Upvotes: 2