Reputation: 357
How do I write a concept that detects a pointer to an arithmetic type?
template <typename T>
concept arithmetic = std::is_arithmetic<T>::value;
template <typename T>
concept pointer_to_arithmetic = requires (T a) {
{ *a } -> arithmetic;
};
template <typename T>
void fn() {
printf("fail\n");
}
template <pointer_to_arithmetic T>
void fn() {
printf("pass\n");
}
struct s{};
int main() {
fn<int>();
fn<int*>();
fn<s>();
fn<s*>();
}
I tried the above and it compiles but doesn't do what it's supposed to.
Expected output is:
fail
pass
fail
fail
Instead I get:
fail
fail
fail
fail
It also doesn't work if I replace *a
with a[0]
.
Upvotes: 4
Views: 181
Reputation: 170163
For an expression E
in a compound requirement, the type constraint predicate is fed decltype((E))
1.
decltype
encodes the value category of the expression in the type it deduces. Since *p
is an lvalue expression. The deduced type is T&
for some T
.
So you may want to rewrite your pair of concepts as
template <typename T>
concept arithmetic_ref = std::is_arithmetic<std::remove_reference_t<T>>::value;
template <typename T>
concept pointer_to_arithmetic = requires (T a) {
{ *a } -> arithmetic_ref ;
};
The atomic predicate could probably be better named.
Of course, this leaves a couple of questions open. Are you just duck-typing, and so any pointer-like type (even std::optional
has operator*
) is permissible? Or are you after only fundamental pointer types? How should the concept treat cv-qualified types (it currently doesn't permit them)?
Depending on how you answer those questions, the concept could be tweaked further.
Upvotes: 5
Reputation: 13925
This should do the trick:
template <typename T>
concept pointer_to_arithmetic = requires (T a) {
requires std::is_arithmetic_v<std::remove_cvref_t<decltype(*a)>>;
requires std::is_pointer_v<T>;
};
...
pointer_to_arithmetic<int> -> false
pointer_to_arithmetic<float> -> false
pointer_to_arithmetic<std::string*> -> false
pointer_to_arithmetic<int*> -> true
pointer_to_arithmetic<float*> -> true
The first line detects any type that can be dereferenced to an arithmetic type. Which might not what you need. So you need to add a second line that detects if T
is indeed a pointer type.
Upvotes: 1
Reputation: 61970
The types given to your arithmetic
trait are int&
and s&
, not int
and s
. One way you can fix this is to remove the reference from the *a
expression:
template<typename T>
auto decay(T&& t) -> std::remove_cvref_t<T> {
return t;
}
template <typename T>
concept pointer_to_arithmetic = requires (T a) {
{ decay(*a) } -> arithmetic;
};
Another way to go about this is to have arithmetic
recognize references to arithmetic types as arithmetic because you can still use the variables in the same way. This is covered in another answer, albeit with a change to the concept name.
Upvotes: 0