Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
468 views
Welcome To Ask or Share your Answers For Others

1 Answer

You say

template<typename T> // requires (std::integral<T> || std::floating_point<T>) // optional
struct Angle;
template<std::integral T> struct Angle<T> { T m_degrees; };
template<std::floating_point T> struct Angle<T> { T m_radians; };

The template needs to be declared with a big enough domain to contain all of its specializations.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...