I have a class that can accept arithmetic types and std::complex. A simplified code of the class is
#include <complex>
template<typename T> struct is_complex : std::false_type {};
template<typename T> struct is_complex<std::complex<T>> : std::true_type {};
template<class T>
struct Foo {
void foo(typename T::value_type t)
requires (is_complex<T>::value) {
}
};
Now, I would like to take the internal type of std::complex
and use it as the type of the parameters in the foo
function.For example, if T is std::complex<double>, then I want the parameter types to be double
.
This function should only be available when T is indeed std::complex
.
I thought I could use typename T::value_type
as the parameter type, since std::complex
has a typedef value_type
. Plus, I thought using requires
here would avoid T to be substitued in this function in case T wasn't std::complex. Silly me.
The issue is that whenever I create a Foo<FundamentalType>
the code breaks, since fundamentals don't have ::value_type
.
int main() {
Foo<int> obj; // Breaks the code.
//obj.foo(4); // Function shouldn't be considered in overload resolution ideally...
Foo<std::complex<int>> obj2; // Works
obj2.foo(4); // Works as expected
}
Ideally, I would like the substitution of T to be ignored for this function in case T is not std::complex. Is that possible? If not, how can I circumvent this?
CodePudding user response:
You're on the right track with is_complex
: you'd like the same here, but with a different body of the type. For example,
template<typename T> struct complex_value_type {};
template<typename T> struct complex_value_type<std::complex<T>> { using type = T; };
template<typename T>
using complex_value_type_t = typename complex_value_type_t<T>;
Then, at any point, you can call it as complex_value_type_t<T>
:
template<class T>
struct Foo {
void foo(complex_value_type_t<T> t)
requires (is_complex<T>::value) {
}
};
The requires is not absolutely necessary then; it's already covered by complex_value_type_t<T>
being defined only for complex<T>
.
CodePudding user response:
You just need some type to put in there, until requires
can disable the function.
I would do this:
struct nullptr_value_type {using value_type = std::nullptr_t;};
using elem_or_null_t = typename std::conditional_t<is_complex<T>::value, T, nullptr_value_type>::value_type;
void foo(elem_or_null_t t)
requires (is_complex<T>::value)
{}
CodePudding user response:
Use a template class as a template parameter.
#include <complex>
template<template<class> class T> struct is_complex : std::false_type {};
template<> struct is_complex<std::complex> : std::true_type {};
template<template<class> class T>
struct Foo {
void foo(typename T<double>::value_type t)//could be typename<T<TT>> if you made foo a templated function
requires (is_complex<T>::value) {
}
};
int main(){
Foo<std::complex> f;
};
But you will need what to put into it when used. I just hard-coded double
but you have to add a new template parameter to Foo
or make foo
a templated member function