I am trying to learn concepts in C++20 and I have a class that represents a data sample. I want to restrict this class to accept only floating point types, or complex floating point types but I can't seem to figure out how to handle complex values with concepts.
Without concepts this is simple, but it allows way too many other data types that I don't want to allow.
Example without concepts:
template <typename T>
class Sample
{
// ...
};
int main()
{
// This compiles
Sample<double> s1;
Sample<complex<double>> s2;
// This also compiles (but I don't want it to!)
Sample<int> s3;
// This compiles as well (again, I don't want it to!)
Sample<complex<int>> s4;
}
With concepts I can easily restrict it to just take floating point values but then it doesn't work with complex values.
template<floating_point T>
class Sample
{
// ...
};
int main()
{
// This compiles
Sample<double> s1;
Sample<float> s2;
// This does NOT compile (but I do want it to!)
Sample<complex<double>> s3;
}
How can I create a concept to restrict the template to work with both real and complex floating point values?
Here's one solution that uses a partial specialization to check if T
is a specialization of std::complex
for floating point types:
template <typename T>
struct is_complex : std::false_type {};
template <std::floating_point T>
struct is_complex<std::complex<T>> : std::true_type {};
With this, you can write the concept:
template <typename T>
concept complex = std::floating_point<T> || is_complex<T>::value;
Here's a demo .
Here's code using a helper type trait class with partial specialization, to determine if a type is complex with floating point coordinates.
#include <type_traits>
#include <concepts>
#include <complex>
template <typename T>
struct is_complex_floating_point : public std::false_type {};
template <typename T>
struct is_complex_floating_point<std::complex<T>>
: public std::bool_constant<std::is_floating_point_v<T>>
{};
template <typename T>
concept real_or_complex_floating_point =
std::floating_point<T> ||
is_complex_floating_point<std::remove_const_t<T>>::value;
template<real_or_complex_floating_point T>
class Sample
{
// ...
};
I used the remove_const_t
because std::floating_point
is satisfied by const float
, etc., meaning your existing Sample
(with constrained parameter) would allow Sample<const double>
, etc. So the concept is defined to accept const std::complex<T>
, making Sample<const std::complex<double>>
etc. work. If that shouldn't be considered valid, you can remove the remove_const_t
part and possibly consider also restricting your template to forbid cv-qualified types.
[As @cigien noticed in their solution , the partial specialization of is_complex_floating_point
is simpler to write using the std::floating_point
concept. An exercise for the reader. ;) ]
A little experimentation shows that you can do this:
template <class T>
concept is_floating_point_or_complex = std::is_floating_point_v<T> || std::is_same_v <T, std::complex <double>>;
template<is_floating_point_or_complex T>
class Sample
{
// ...
};
But it's not obvious how to avoid specialising std::complex
in is_floating_point_or_complex
(if indeed you want to).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.