@makogan asked (buried 19 deep in comments): what if func has arguments?
The answer is: for simple cases, manufacture parameters using constructors, or new expression. (Not particularly readable, but way more readable than the probably correct way given below).
template <typename T>
concept HasFunc1 =
requires(T t) {
{ t.func1( int() ) } -> std::same_as<int>;
};
For more complex examples, you can declare test parameters in the requires clause argument list:
concept IsCoServiceImplementation = requires(
T t,
CoServiceReply<typename T::return_type> *completionCallback)
{
{ T::return_type };
{t.OnSuspend(completionCallback) };
{t.OnCancel(completionCallback) };
};
This concept does have the desired intent (for me): it mostly converts nested-10-deep error messages about failures to meet a template contract into almost readable first-level error messages.
There's still a strange disconnect between the required code and the concept. It would be really nice to have tighter constraints on parameter types. And testing const-ness is grimly difficult. Far from the feature I had hope for. :-(
I'm still struggling with c++20 features. I'm open to suggestions as to how to do this better.
(A CoService, in case you're wondering, is an experimental attempt I'm working on to make it easier to marshal coroutine code back onto non-coroutine code with minimum fuss and bother).