I have been trying to write a trait which figures out whether some callable takes a rvalue reference as its first parameter. This lets some metaprogramming adjust whether move or copy semantics are used when calling the callable where the callable is supplied by external code (effectively one is overloading on the callable type supplied by a user).
#include <functional>
#include <iostream>
#include <type_traits>
// Does the callable when called with Arg move?
template<class F, class Arg> struct is_callable_moving
{
typedef typename std::decay<Arg>::type arg_type;
typedef typename std::function<F(arg_type)>::argument_type parameter_type;
static constexpr bool value = std::is_rvalue_reference<parameter_type>::value;
};
int main(void)
{
auto normal = [](auto) {}; // Takes an unconstrained input.
auto moving = [](auto&&) {}; // Takes a constrained to rvalue ref input.
std::cout << "normal=" << is_callable_moving<decltype(normal), int>::value << std::endl;
std::cout << "moving=" << is_callable_moving<decltype(moving), int>::value << std::endl; // should be 1, but isn't
getchar();
return 0;
}
The above obviously does not work, but it hopefully explains what I am looking for: I want to detect callables which constrain their parameter to only being a rvalue reference.
Note that other Stack Overflow answers such as Get lambda parameter type aren't useful here because I need to support C++ 14 generic lambdas (i.e. the ones taking auto parameters) and therefore tricks based on taking the address of the call operator inside the lambda type will fail with inability to resolve overload.
You will note that is_callable_working takes an Arg type, and the correct overload of the callable F would be found via F(Arg)
. The thing I'd like to detect is whether the available overload for F(Arg)
is a F::operator()(Arg &&)
or a F::operator()(
<any other reference type for Arg>)
. I would imagine that if ambiguous overloads for F()
are available e.g. both F(Arg)
and F(Arg &&)
then the compiler would error out, however a [](auto)
should not be ambiguous from [](auto &&)
.
Edit: Clarified my question hopefully. I'm really asking if C++ metaprogramming can detect constraints on arguments.
Edit 2: Here is some more clarification. My exact use case is this:
template<class T> class monad
{
...
template<class U> monad<...> bind(U &&v);
};
where monad<T>.bind([](T}{})
takes T
by copy, and I'd like monad<T>.bind([](T &&){})
takes T
by rvalue reference (i.e. the callable could move from it).
As inferred above, I'd also like monad<T>.bind([](auto){})
to take T by copy, and monad<T>.bind([](auto &&){})
to take T by rvalue reference.
As I mentioned, this is a sort of overload of monad<T>.bind()
whereby different effects occur depending on how the callable is specified. If one were able to overload bind()
based on call signature as we could before lambdas, all this would be easy. It's dealing with the unknowability of capturing lambda types which is the problem here.
auto&&
is the latter (it binds to both rvalues and lvalues). – Dennadennardauto moving = [](auto&&) {}; // Takes a constrained to rvalue ref input.
-- this is not constrained to rvalue ref input. What the callable consumes shouldn't matter to the calling code. If you (and others) aren't using the data again, you should always move. If you (and others) are using the data again, you should not move. This is information about the data you are calling with, not about the thing you are calling. Andforward
andreference_wrapper
exist to deal with the corner cases. Can you be less abstract in what you want? What concrete problem are you solving? – Subshrubfirst_arg</* stuff */>::result
is. – Kreislerauto m = monad<int>.bind([](auto&&x){std::cout<<decltype(x)(x)<<'\n';}); int x = 3; m(x);
-- from what I can tell, you are asking for the code I just wrote should fail to compile. This seems like a bad idea. But maybe I misunderstood you, because you are not being very concrete. You are focusing on narrow technical problems, which is great, but your problem also needs to be practical. – Subshrub&F::operator()
into a context that causes overload resolution and template argument deduction to happen. I'll write a full answer later when I get back to my computer. – Kreisler