This question demonstrates how to use C++20 concepts to choose overloads for a function template. I'm trying to do something analogous: choose specializations for a class template.
I'm starting with a class template for Angle<T>
which wraps a floating point value containing an angle in radians. Using concepts, I can ensure that users don't instantiate Angle
with anything other than a floating point type:
template <std::floating_point T> struct Angle { T m_radians; };
Later, I decided I'd like to let clients use a distinct implementation of Angle<T>
that can handle integral types. In other words, I'd like to allow code like:
const auto theta = Angle<float>(3.14f);
const auto phi = Angle<int>(180);
So I tried adding a comparable template.
template <std::integral T> struct Angle { T m_degrees; };
The compilers view this additional implementation as a redeclaration of the template with a different constraint. I've tried several different ways of expressing my intent, but none satisfy any of the compilers I've tried. In fact, I cannot even find a way to do this with std::enable_if
and traditional SFINAE--admittedly, it's entirely possible I don't understand SFINAE very well.
The only approach I've found requires making distinct specializations for each of the integral and floating point types.
template <std::floating_point T> struct AngleRad { T m_radians; };
template <std::integral T> struct AngleDeg { T m_degrees; };
template <typename T> struct Angle2 {};
template <> struct Angle2<float> : public AngleRad<float> {};
template <> struct Angle2<double> : public AngleRad<double> {};
template <> struct Angle2<long double> : public AngleRad<long double> {};
template <> struct Angle2<short> : public AngleDeg<short> {};
template <> struct Angle2<int> : public AngleDeg<int> {};
template <> struct Angle2<long> : public AngleDeg<long> {};
template <> struct Angle2<long long> : public AngleDeg<long long> {};
template <> struct Angle2<unsigned short> : public AngleDeg<unsigned short> {};
template <> struct Angle2<unsigned int> : public AngleDeg<unsigned int> {};
template <> struct Angle2<unsigned long> : public AngleDeg<unsigned long> {};
template <> struct Angle2<unsigned long long> : public AngleDeg<unsigned long long> {};
[Yes, I know there are a few more integral types. I'm just trying to illustrate the point. This example is contrived for simplicity, though it's inspired by actual code.]
Is there a way to use concepts to express this more simply?
See Question&Answers more detail:os