FYI: C++17 std::is_invocable_v does exactly what i was expecting.
Imagine a concept to check if the invocation of a callable object is possible with specific argument types:
template <typename Fn, typename... Args>
concept has_request_interface = requires (Fn request, Args&&... args)
{
{ std::invoke(request, std::forward<Args>(args)...) }-> Status;
};
versus
template <typename Fn, typename... Args>
concept has_request_interface = requires (Fn request, Args... args)
{
{ std::invoke(request, args...) }-> Status;
};
Is it meaningful using perfect forwarding in requires expressions ?
It seems to me that the answer is yes because the request callable object may expect rvalues for some arguments.
But does the requires (Fn request, Args... args)
behave as a function declaration regarding the lvalue nature of args...
?
What is Perfect Forwarding. Perfect forwarding allows a template function that accepts a set of arguments to forward these arguments to another function whilst retaining the lvalue or rvalue nature of the original function arguments.
When t is a forwarding reference (a function argument that is declared as an rvalue reference to a cv-unqualified function template parameter), this overload forwards the argument to another function with the value category it had when passed to the calling function.
std::forward The need for this function stems from the fact that all named values (such as function parameters) always evaluate as lvalues (even those declared as rvalue references), and this poses difficulties in preserving potential move semantics on template functions that forward arguments to other functions.
Let's define a few concepts in this post. A concept can be defined by a function template or by a variable template. A variable template is new with C++14 and declares a family of variables. If you use a function template for your concept, it's called a function concept; in the second case a variable concept.
It will behave exactly like what it looks like. That's kind of the point of a requires
expression: to make these things look like C++. So it will behave like C++.
What matters is how you use the concept. That is, when you requires
some template based on it, you should invoke the concept correctly. For example:
template<typename Func, typename ...Args
void constrained(Func func, Args &&...args)
requires has_request_interface<Func, Args...>
{
Status status = func(std::forward<Args>(args)...);
}
So yes, if you want forwarding through the concept to work, your concept needs to use &&
and forwarding.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With