Reputation: 63
I want to create a struct called Vec2 which looks something like this:
template<typename T>
struct Vec2 {
T x, y;
// Other things are also there like Constructors, Destructors, operator overloads, etc.
};
I want to be able to create an operator overload for the % operator. The overload looks something like this
Vec2<T> operator%(const Vec2<T> &other) {
return Vec2<T>(this-> x % other.x, this.y % other.y);
}
Now the problem here is that if someone instantiates this struct with a template parameter of float then this operator overload breaks as you can't modulo floats with the default % operator in C++. So in that case I wanted to use this function instead.
Vec2<T> operator%(const Floating &other) {
return Vec2<T>(fmod(this->x, other.x), fmod(this->y, other.y));
}
So what can I do so that if someone instantiates this struct with with float as a template param then they should have access to the second operator overload otherwise the first.
I've managed to create a template that helps me filter out all float template parameters like this
template<typename T,
typename = std::enable_if_t<std::is_floating_point_v<Integral>>>
struct Vec2 {
T x, y;
// Other things are also there like Constructors, Destructors, operator overloads, etc.
Vec2<T> operator%(const Vec2<T> &other) {
return Vec2<T>(fmod(this->x, other.x), fmod(this->y, other.y));
}
};
This makes it so that no one can instantiate this class with an Integral value. But I don't want this to happen. I want them to be able to instantiate this class also with an Integral value, just with a different member function. How can achieve this? I've tried doing some partial specialization but it didn't work out.
Note: I have a slightly different structure in my actual code with a base class containing all common code but I didn't include it over here for simplicity.
I'm using GCC 11.1.0. The C++ standard doesn't really matter as I can even use C++20.
Upvotes: 0
Views: 46
Reputation: 11350
In this case a simple if constexpr
should be enough:
Vec2<T> operator%(const Vec2<T> &other)
{
if constexpr (std::is_floating_point_v<T>)
{
return Vec2<T>(fmod(this->x, other.x), fmod(this->y, other.y));
}
else
{
return Vec2<T>(this-> x % other.x, this->y % other.y);
}
}
Here I used std::is_floating_point_v
to determine at compile time wether T
is any floating point type or not. Only one of the two branches will be present in the respective template instantiation.
I think that's far simpler than specializing the whole template for float/non-float and in my opinion more readable than SFINAE.
Upvotes: 1