Reputation: 608
I am teaching myself the new concepts feature of C++20 and I hit a snag when I was trying to ensure that the member function that has to be present only accepts specific types. I found this question which showed me how to ensure any function can be present. Then I wanted to take it a step further by ensuring a function with specific parameters are present. I started by defining that the type T
must have a function number()
that returns an int
#include <concepts>
template <typename T>
concept MyRequirement = requires (T t)
{
{ t.number(int) } -> std::same_as<int>;
};
template <typename T>
requires MyRequirement<T>
struct RequiresBool
{
T t;
};
struct GoodType
{
int number(int x) { return x; };
};
int main()
{
RequiresBool<GoodType> good_object;
}
but gcc gave me the following error:
<source>:7:16: error: expected primary-expression before 'int'
7 | { t.number(int) } -> std::same_as<int>;
| ^~~"
So I changed it to the following which worked.
template <typename T>
concept MyRequirement = requires (T t, int x)
{
{ t.number(x) } -> std::same_as<int>;
};
However, when I change the function signature in GoodType
to int number(double x)
, it still works. Sure, a double
can be implicitly converted to an int
and this would also work for normal functions. I could get around this by declaring (but not defining) int number(double);
in GoodType
which would give me an error during compilation, but this puts the responsibility on whoever writes the template class to remember to do this.
This works fine with other types, e.g.
template <typename T>
concept MyRequirement = requires (T t, std::string x)
{
{ t.number(x) } -> std::same_as<int>; //int number(int x) is now invalid
};
but the integers/doubles give me a headache.
Is some way ensure that the type of x in MyRequirement
HAS to be an integer using C++20 concepts?
Upvotes: 2
Views: 214
Reputation: 20619
A compound requirement ({ expression}
) checks if the expression is well-formed, so implicit type conversions occur. An int
can be implicitly converted to a double
, but a std::string
cannot be implicitly converted to an int
.
You can use the pointer to member in a compound requirement:
template <typename T>
concept MyRequirement = requires {
{ &T::number } -> std::same_as<int (T::*)(int)>;
};
Upvotes: 3
Reputation: 12968
I'm not to familiar with concepts yet, so there might be a more idiomatic way. But this felt intuitive to me and works.
#include <type_traits>
template <typename T>
concept MyRequirement = std::is_same_v<decltype(&T::number), int (T::*)(int)>;
This covers both return type and parameter.
If it's not familiar, int (T::*)(int)
is a pointer-to-member-function with signature int(int)
belonging to class T
.
decltype(&T::number)
gets the type of a pointer to T
s member number
.
Upvotes: 3