The task is to create a single-argument function that forwards all types apart from one (Foo), which it converts (to Bar).
(Let us assume there exists a conversion from Foo to Bar).
Here is the usage scenario:
template<typename Args...>
void f( Args... args )
{
g( process<Args>(args)... );
}
(I've tried to extract/simplify it from the original context here. -- if I've made a mistake, please someone tell me!)
Here are two possible implementations:
template<typename T>
T&& process(T&& t) {
return std::forward<T>(t);
}
Bar process(Foo x) {
return Bar{x};
}
And...
template <typename T, typename U>
T&& process(U&& u) {
return std::forward<T>(std::forward<U>(u));
}
template <typename T>
Bar process(Foo x) {
return Bar{x};
}
I have it on good authority (here) that the second one is preferable.
However, I can't understand the given explanation. I think this is delving into some of the darkest corners of C++.
I think I'm missing machinery necessary to understand what's going on. Could someone explain in detail? If it is too much digging, could anyone recommend a resource for learning the necessary prerequisite concepts?
EDIT: I would like to add that in my particular case the function signature is going to match one of the typedef-s on this page. That is to say, every argument is going to be either PyObject* (with PyObject being an ordinary C struct) or some basic C type like const char*, int, float. So my guess is that the lightweight implementation may be most appropriate (I'm not a fan of over-generalising). But I am really interested in acquiring the right mindset to solve such problems as these.
I sense a minor misconception in your understanding of the use case you are facing.
First of all, this is a function template:
struct A
{
template <typename... Args>
void f(Args... args)
{
}
};
And this is not a function template:
template <typename... Args>
struct A
{
void f(Args... args)
{
}
};
In the former definition (with a function template) the argument type deduction takes place. In the latter, there is no type deduction.
You aren't using a function template. You're using a non-template member function from a class template, and for this particular member function its signature is fixed.
By defining your trap class like below:
template <typename T, T t>
struct trap;
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R call(Args... args);
};
and referring to its member function like below:
&trap<decltype(&Base::target), &Base::target>::call;
you end up with a pointer to a static non-template call function with a fixed signature, identical to the signature of the target function.
Now, that call function serves as an intermediate invoker. You will be calling the call function, and that function will call the target member function, passing its own arguments to initialize target's parameters, say:
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R call(Args... args)
{
return (get_base()->*t)(args...);
}
};
Suppose the target function used to instantiate the trap class template is defined as follows:
struct Base
{
int target(Noisy& a, Noisy b);
};
By instantiating the trap class you end up with the following call function:
// what the compiler *sees*
static int call(Noisy& a, Noisy b)
{
return get_base()->target(a, b);
}
Luckily, a is passed by reference, it is just forwarded and bound by the same kind of reference in the target's parameter. Unfortunately, this doesn't hold for the b object - no matter if the Noisy class is movable or not, you're making multiple copies of the b instance, since that one is passed by value:
the first one: when the call function is invoked itself from an external context.
the second one: to copy b instance when calling the target function from the body of call.
DEMO 1
This is somewhat inefficient: you could have saved at least one copy-constructor call, turning it into a move-constructor call if only you could turn the b instance into an xvalue:
static int call(Noisy& a, Noisy b)
{
return get_base()->target(a, std::move(b));
// ~~~~~~~~~~~^
}
Now it would call a move constructor instead for the second parameter.
So far so good, but that was done manually (std::move added knowing that it's safe to apply the move semantics). Now, the question is, how could the same functionality be applied when operating on a parameter pack?:
return get_base()->target(std::move(args)...); // WRONG!
You can't apply std::move call to each and every argument within the parameter pack. This would probably cause compiler errors if applied equally to all arguments.
DEMO 2
Fortunately, even though Args... is not a forwarding-reference, the std::forward helper function can be used instead. That is, depending on what the <T> type is in std::forward<T> (an lvalue reference or a non-lvalue-reference) the std::forward will behave differently:
for lvalue references (e.g. if T is Noisy&): the value category of the expression remains an lvalue (i.e. Noisy&).
for non-lvalue-references (e.g. if T is Noisy&& or a plain Noisy): the value category of the expression becomes an xvalue (i.e. Noisy&&).
Having that said, by defining the target function like below:
static R call(Args... args)
{
return (get_base()->*t)(std::forward<Args>(args)...);
}
you end up with:
static int call(Noisy& a, Noisy b)
{
// what the compiler *sees*
return get_base()->target(std::forward<Noisy&>(a), std::forward<Noisy>(b));
}
turning the value category of the expression involving b into an xvalue of b, which is Noisy&&. This lets the compiler pick the move constructor to initialize the second parameter of the target function, leaving a intact.
DEMO 3 (compare the output with DEMO 1)
Basically, this is what the std::forward is for. Usually, std::forward is used with a forwarding-reference, where T holds the type deduced according to the rules of type deduction for forwarding references. Note that it always requires from you to pass over the <T> part explicitly, since it will apply a different behavior depending on that type (not depending on the value category of its argument). Without the explicit type template argument <T>, std::forward would always deduce lvalue references for arguments referred to through their names (like when expanding the parameter pack).
Now, you wanted to additionally convert some of the arguments from one type to another, while forwarding all others. If you don't care about the trick with std::forwarding arguments from the parameter pack, and it's fine to always call a copy-constructor, then your version is OK:
template <typename T> // transparent function
T&& process(T&& t) {
return std::forward<T>(t);
}
Bar process(Foo x) { // overload for specific type of arguments
return Bar{x};
}
//...
get_base()->target(process(args)...);
DEMO 4
However, if you want to avoid the copy of that Noisy argument in the demo, you need to somehow combine std::forward call with the process call, and pass over the Args types, so that std::forward could apply proper behavior (turning into xvalues or not doing anything). I just gave you a simple example of how this could be implemented:
template <typename T, typename U>
T&& process(U&& u) {
return std::forward<T>(std::forward<U>(u));
}
template <typename T>
Bar process(Foo x) {
return Bar{x};
}
//...
get_base()->target(process<Args>(args)...);
But this is just one of the options. It can be simplified, rewritten, or reordered, so that std::forward is called before you call the process function (your version):
get_base()->target(process(std::forward<Args>(args))...);
DEMO 5 (compare the output with DEMO 4)
And it will work fine as well (that is, with your version). So the point is, the additional std::forward is just to optimize your code a little, and the provided idea was just one of the possible implementations of that functionality (as you can see, it brings about the same effect).
Would not be the first part of Version 2 sufficient? only:
template <typename T, typename U>
T&& process(U&& u) {
return std::forward<T>(std::forward<U>(u));
}
Given a usage case with an existing conversion (constructor for "Bar" from "Foo"), like:
struct Foo {
int x;
};
struct Bar {
int y;
Bar(Foo f) {
y = f.x;
}
};
int main() {
auto b = process<Bar>(Foo()); // b will become a "Bar"
auto i = process<int>(1.5f);
}
You are forced to specify the first template parameter (the type to convert to) anyway because the compiler cannot deduce it. So it knows what type you expect and will construct a temporary object of type "Bar" because there is a constructor.
Related
I've scrolled and searched through the standard and cppreference for hours to no avail, would really appreciate if someone could explain this occurance for me:
I am looking at the standard concept std::convertibe_to. Here's a simple example that I do understand
class A {};
class B : public A {};
std::convertible_to<A, B>; // false
std::convertible_to<B, A>; // true
Works as expected.
Now there is also another possible way to use it, that I don't quite understand
void foo(std::convertible_to<A> auto x) { /* ... */ }
, and this function can easily accept any type convertible to A. This is weird though, because the first template parameter ("From") is essencially dropped, and deduced on function call. This following function would also work, and I'm fairly certain it's actually equivalent to the previous one
template<typename T, std::convertible_to<T> S>
void foo(S x) { /* ... */ }
again the type of x is deduced when we call foo.
This works, despite the template requiring two parameters. I tried also with std::derived_from and it seems to work. This form of specifying a concept with only one template parameter even appears in the standard itself, so there must be some piece of syntax that explains it.
Notice that the only version of std::convertible_to that exists is in fact one that takes two template parameters.
Could anyone clarify why this works?
void foo( constraint<P0, P1, P2> auto x );
this translates roughly to
template<contraint<P0, P1, P2> X>
void foo( X x );
which translates roughly to
template<class X> requires constraint<X, P0, P1, P2>
void foo( X x );
notice how the type X is prepended to the template arguments of the constraint.
So in your case,
template<typename T, std::convertible_to<T> S>
void foo(S x) { /* ... */ }
is roughly
template<typename T, class S>
requires std::convertible_to<S, T>
void foo(S x) { /* ... */ }
(I say roughly, because I believe they are not exactly equivalent in subtle ways. For example, the second one introduces the name X, while the first does not. And there are probably other differences of similar scale; what I mean is that understanding the translation will give you an understanding of what is translated. This is unlike for(:) loop-for(;;) loop correspondence; the standard specifies for(:) loops in terms of for(;;) loops, which isn't what I'm claiming above.)
There are several locations where a concept name can be used where the first argument to the template concept is not supplied in the template argument list. Constraining an auto deduced variable is one of them.
The first argument in these cases is provided by some expression, typically using template argument deduction rules. In the case of a constrained function parameter, the first argument is determined by the template function itself. That is, if you call foo(10), template argument deduction will deduce the auto template parameter as an int. Therefore, the full concept will be convertible_to<int, A>.
Thanks to decltype as a return type, C++11 made it extremely easy to introduce decorators. For instance, consider this class:
struct base
{
void fun(unsigned) {}
};
I want to decorate it with additional features, and since I will do that several times with different kinds of decorations, I first introduce a decorator class that simply forwards everything to base. In the real code, this is done via std::shared_ptr so that I can remove decorations and recover the "naked" object, and everything is templated.
#include <utility> // std::forward
struct decorator
{
base b;
template <typename... Args>
auto
fun(Args&&... args)
-> decltype(b.fun(std::forward<Args>(args)...))
{
return b.fun(std::forward<Args>(args)...);
}
};
Perfect forwarding and decltype are just wonderful. In the real code, I actually use a macro that just needs the name of the function, all the rest is boilerplate.
And then, I can introduce a derived class that adds features to my object (derived is improper, agreed, but it helps understanding that derived is-a-kind of base, albeit not via inheritance).
struct foo_t {};
struct derived : decorator
{
using decorator::fun; // I want "native" fun, and decorated fun.
void fun(const foo_t&) {}
};
int main()
{
derived d;
d.fun(foo_t{});
}
Then C++14 came, with return type deduction, which allows to write things in a simpler way: remove the decltype part of the forwarding function:
struct decorator
{
base b;
template <typename... Args>
auto
fun(Args&&... args)
{
return b.fun(std::forward<Args>(args)...);
}
};
And then it breaks. Yes, at least according to both GCC and Clang, this:
template <typename... Args>
auto
fun(Args&&... args)
-> decltype(b.fun(std::forward<Args>(args)...))
{
return b.fun(std::forward<Args>(args)...);
}
};
is not equivalent to this (and the issue is not auto vs. decltype(auto)):
template <typename... Args>
auto
fun(Args&&... args)
{
return b.fun(std::forward<Args>(args)...);
}
};
The overload resolution seems to be completely different and it ends like this:
clang++-mp-3.5 -std=c++1y main.cc
main.cc:19:18: error: no viable conversion from 'foo_t' to 'unsigned int'
return b.fun(std::forward<Args>(args)...);
^~~~~~~~~~~~~~~~~~~~~~~~
main.cc:32:5: note: in instantiation of function template specialization
'decorator::fun<foo_t>' requested here
d.fun(foo_t{});
^
main.cc:7:20: note: passing argument to parameter here
void fun(unsigned) {}
^
I understand the failure: my call (d.fun(foo_t{})) does not match perfectly with the signature of derived::fun, which takes a const foo_t&, so the very eager decorator::fun kicks in (we know how Args&&... is extremely impatient to bind to anything that does not match perfectly). So it forwards this to base::fun which can't deal with foo_t.
If I change derived::fun to take a foo_t instead of const foo_t&, then it works as expected, which shows that indeed here the problem is that there is a competition between derived::fun and decorator::fun.
However why the heck does this show with return-type deduction??? And more precisely why was this behavior chosen by the committee?
To make things easier, on Coliru:
the version with decltype that works
the version with type deduction that fails
Thanks!
Just look at this call:
d.fun(foo_t{});
You create a temporary (i.e rvalue), passing it as argument to the function. Now what do you think happens?
It first attempts to bind to the argument Arg&&, as it can accept rvalue but due to invalid return type deduction (which again due to foo_t cannot convert into unsigned int, because of which b.fun(std::forward<Args>(args)...) turns out to be invalid expression), this function is rejected if you use the decltype(expr) as return type, as in this case SFINAE comes into picture. But if you use simply auto, then SFINAE doesn't come into picture and the error is classified to be a hard-error which results in compilation failure.
The second overload which accepts foo_t const& as argument is called if SFINAE works in the first case.
Given the following code, what is the reason behind the ambiguity? Can I circumvent it or will I have to keep the (annoying) explicit casts?
#include <functional>
using namespace std;
int a(const function<int ()>& f)
{
return f();
}
int a(const function<int (int)>& f)
{
return f(0);
}
int x() { return 22; }
int y(int) { return 44; }
int main()
{
a(x); // Call is ambiguous.
a(y); // Call is ambiguous.
a((function<int ()>)x); // Works.
a((function<int (int)>)y); // Works.
return 0;
}
Interestingly, if I comment out the a() function with the function<int ()> parameter and call a(x) in my main, the compilation correctly fails because of the type mismatch between x and the argument function<int (int)> of the only a() function available. If the compiler fails in that case, why would there be any ambiguity when the two a() functions are present?
I've tried with VS2010 and g++ v. 4.5. Both give me the exact same ambiguity.
The problem is that both function<int()> and function<int(int)> are constructible from the same function. This is what the constructor declaration of std::function looks like in VS2010:
template<class _Fx>
function(_Fx _Func, typename _Not_integral<!_Is_integral<_Fx>::value, int>::_Type = 0);
Ignoring the SFINAE part, it is constructible from pretty much anything.
std::/boost::function employ a technique called type erasure, to allow arbitary objects/functions to be passed in, so long they satisfy the signature when being called. One drawback from that is, that you get an error in the deepest part of the implementation (where the saved function is being called) when supplying an object which can't be called like the signature wants it to, instead of in the constructor.
The problem can be illustrated with this little class:
template<class Signature>
class myfunc{
public:
template<class Func>
myfunc(Func a_func){
// ...
}
};
Now, when the compiler searches for valid functions for the overload set, it tries to convert the arguments if no perfect fitting function exists. The conversion can happen through the constructor of the parameter of the function, or through a conversion operator of the argument given to the function. In our case, it's the former.
The compiler tries the first overload of a. To make it viable, it needs to make a conversion. To convert a int(*)() to a myfunc<int()>, it tries the constructor of myfunc. Being a template that takes anything, the conversion naturally succeeds.
Now it tries the same with the second overload. The constructor still being the same and still taking anything given to it, the conversion works too.
Being left with 2 functions in the overload set, the compiler is a sad panda and doesn't know what to do, so it simply says the call is ambigious.
So in the end, the Signature part of the template does belong to the type when making declarations/definitions, but doesn't when you want to construct an object.
Edit:
With all my attention on answering the title-question, I totally forgot about your second question. :(
Can I circumvent it or will I have to keep the (annoying) explicit casts?
Afaik, you have 3 options.
Keep the cast
Make a function object of the appropriate type and pass that
function<int()> fx = x;
function<int(int)> fy = y;
a(fx);
a(fy);
Hide the tedious casting in a function and use TMP to get the right signature
The TMP (template metaprogramming) version is quite verbose and with boilerplate code, but it hides the casting from the client. An example version can be found here, which relies on the get_signature metafunction that is partially specialized on function pointer types (and provides a nice example how pattern matching can work in C++):
template<class F>
struct get_signature;
template<class R>
struct get_signature<R(*)()>{
typedef R type();
};
template<class R, class A1>
struct get_signature<R(*)(A1)>{
typedef R type(A1);
};
Of course, this needs to be extended for the number of arguments you want to support, but that is done once and then buried in a "get_signature.h" header. :)
Another option I consider but immediatly discarded was SFINAE, which would introduce even more boilerplate code than the TMP version.
So, yeah, that are the options that I know of. Hope one of them works for you. :)
I've seen this question come up one too many times. libc++ now compiles this code without ambiguity (as a conforming extension).
Overdue Update
This "extension" proved sufficiently popular that it was standardized in C++14 (though I was not personally responsible for getting that job done).
In hindsight, I did not get this extension exactly correct. Earlier this month (2015-05-09) the committee voted in LWG issue 2420 which effectively changes the definition of Callable so that if the std::function has a void return type it will ignore the return type of the wrapped functor, but still otherwise consider it Callable if everything else matches up, instead of considering it not Callable.
This post-C++14 tweak does not impact this particular example since the return types involved are consistently int.
Here's an example of how to wrap std::function in a class that checks invokability of its constructor parameters:
template<typename> struct check_function;
template<typename R, typename... Args>
struct check_function<R(Args...)>: public std::function<R(Args...)> {
template<typename T,
class = typename std::enable_if<
std::is_same<R, void>::value
|| std::is_convertible<
decltype(std::declval<T>()(std::declval<Args>()...)),
R>::value>::type>
check_function(T &&t): std::function<R(Args...)>(std::forward<T>(t)) { }
};
Use like this:
int a(check_function<int ()> f) { return f(); }
int a(check_function<int (int)> f) { return f(0); }
int x() { return 22; }
int y(int) { return 44; }
int main() {
a(x);
a(y);
}
Note that this isn't quite the same as overloading on function signature, as it treats convertible argument (and return) types as equivalent. For exact overloading, this should work:
template<typename> struct check_function_exact;
template<typename R, typename... Args>
struct check_function_exact<R(Args...)>: public std::function<R(Args...)> {
template<typename T,
class = typename std::enable_if<
std::is_convertible<T, R(*)(Args...)>::value>::type>
check_function_exact(T &&t): std::function<R(Args...)>(std::forward<T>(t)) { }
};
std::function<T> has a conversion ctor that takes an arbitrary type (i.e., something other than a T). Sure, in this case, that ctor would result in a type mismatch error, but the compiler doesn't get that far -- the call is ambiguous simply because the ctor exists.
are these functions equivalent?
template <class T>
void foo(T && t)
{
bar(std::forward<T>(t));
}
template <class T>
void foo2(T && t)
{
bar(std::forward<decltype(t)>(t));
}
template <class T>
void foo3(T && t)
{
bar(std::forward(t));
}
if they are, can I always use this macro for perfect forwarding?
#define MY_FORWARD(var) std::forward<decltype(var)>(var)
or just use
bar(std::forward(t));
I believe foo2 and foo3 are same, but I found people are always use forward like foo, is any reason to explicitly write the type?
I understand that T and T&& are two different types, but I think std::forward<T> and std::forward<T&&> always give the same result?
Edit:
the reason I want to use macro is I want to save some typing on following C++1y code, I have many similar code in different places
#define XLC_FORWARD_CAPTURE(var) var(std::forward<decltype(var)>(var))
#define XLC_MOVE_CAPTURE(var) var(std::move(var))
template <class T, class U>
auto foo(T && func, U && para )
{
auto val = // some calculation
return [XLC_FORWARD_CAPTURE(func),
XLC_FORWARD_CAPTURE(para),
XLC_MOVE_CAPTURE(val)](){
// some code use val
func(std::forward<U>(para));
};
}
Are these functions two equivalent?
Yes, they are equivalent. decltype(t) is the same as T&&, and when used with std::forward, there is no difference between T and T&&, regardless what T is.
Can I always use this macro for perfect forwarding?
Yes, you can. If you want to make your code unreadable and unmaintainable, then do so. But I strongly advise against it. On the one hand, you gain basically nothing from using this macro. And on the other hand, other developers have to take a look at the definition to understand it, and it can result in subtle errors. For example adding additional parentheses won't work:
MY_FORWARD((t))
In contrast, the form with decltype is perfectly valid. In particular, it is the preferred way of forwarding parameters from generic lambda expressions, because there are no explicit type parameters:
[](auto&& t) { foobar(std::forward<decltype(t)>(t)); }
I ignored the 3rd variant with std::forward(t), because it isn't valid.
Update: Regarding your example: You can use call-by-value instead of call-by-reference for the function template foo. Then you can use std::move instead of std::forward. This adds two additional moves to the code, but no additional copy operations. On the other hand, the code becomes much cleaner:
template <class T, class U>
auto foo(T func, U para)
{
auto val = // some calculation
return [func=std::move(func),para=std::move(para),val=std::move(val)] {
// some code use val
func(std::move(para));
};
}
The accepted answer does not solve the problem in title completely.
A macro argument preserves the type of the expression. A forwarding parameter in a template does not. This means t in foo2 (as a forwarding function parameter) has the type T&& (because this is the forwarding template parameter), but it can be something different when the macro is in other contexts. For example:
using T = int;
T a = 42;
T&& t(std::move(a));
foo(MY_FORWARD(t)); // Which foo is instantiated?
Note here t is not an xvalue, but an lvalue. With std::forward<T>(t), which is equivalent to std::forward<int>(t), t would be forwarded as an lvalue. However, with MY_FORWARD(t), which is equivalent to std::forward<int&&>(t), t would be forwarded as an xvalue. This contextual-dependent difference is sometime desired when you have to deal with some declared variables with rvalue reference types (not forwarding paramter even they may look like similar in syntax).
Given the following code, what is the reason behind the ambiguity? Can I circumvent it or will I have to keep the (annoying) explicit casts?
#include <functional>
using namespace std;
int a(const function<int ()>& f)
{
return f();
}
int a(const function<int (int)>& f)
{
return f(0);
}
int x() { return 22; }
int y(int) { return 44; }
int main()
{
a(x); // Call is ambiguous.
a(y); // Call is ambiguous.
a((function<int ()>)x); // Works.
a((function<int (int)>)y); // Works.
return 0;
}
Interestingly, if I comment out the a() function with the function<int ()> parameter and call a(x) in my main, the compilation correctly fails because of the type mismatch between x and the argument function<int (int)> of the only a() function available. If the compiler fails in that case, why would there be any ambiguity when the two a() functions are present?
I've tried with VS2010 and g++ v. 4.5. Both give me the exact same ambiguity.
The problem is that both function<int()> and function<int(int)> are constructible from the same function. This is what the constructor declaration of std::function looks like in VS2010:
template<class _Fx>
function(_Fx _Func, typename _Not_integral<!_Is_integral<_Fx>::value, int>::_Type = 0);
Ignoring the SFINAE part, it is constructible from pretty much anything.
std::/boost::function employ a technique called type erasure, to allow arbitary objects/functions to be passed in, so long they satisfy the signature when being called. One drawback from that is, that you get an error in the deepest part of the implementation (where the saved function is being called) when supplying an object which can't be called like the signature wants it to, instead of in the constructor.
The problem can be illustrated with this little class:
template<class Signature>
class myfunc{
public:
template<class Func>
myfunc(Func a_func){
// ...
}
};
Now, when the compiler searches for valid functions for the overload set, it tries to convert the arguments if no perfect fitting function exists. The conversion can happen through the constructor of the parameter of the function, or through a conversion operator of the argument given to the function. In our case, it's the former.
The compiler tries the first overload of a. To make it viable, it needs to make a conversion. To convert a int(*)() to a myfunc<int()>, it tries the constructor of myfunc. Being a template that takes anything, the conversion naturally succeeds.
Now it tries the same with the second overload. The constructor still being the same and still taking anything given to it, the conversion works too.
Being left with 2 functions in the overload set, the compiler is a sad panda and doesn't know what to do, so it simply says the call is ambigious.
So in the end, the Signature part of the template does belong to the type when making declarations/definitions, but doesn't when you want to construct an object.
Edit:
With all my attention on answering the title-question, I totally forgot about your second question. :(
Can I circumvent it or will I have to keep the (annoying) explicit casts?
Afaik, you have 3 options.
Keep the cast
Make a function object of the appropriate type and pass that
function<int()> fx = x;
function<int(int)> fy = y;
a(fx);
a(fy);
Hide the tedious casting in a function and use TMP to get the right signature
The TMP (template metaprogramming) version is quite verbose and with boilerplate code, but it hides the casting from the client. An example version can be found here, which relies on the get_signature metafunction that is partially specialized on function pointer types (and provides a nice example how pattern matching can work in C++):
template<class F>
struct get_signature;
template<class R>
struct get_signature<R(*)()>{
typedef R type();
};
template<class R, class A1>
struct get_signature<R(*)(A1)>{
typedef R type(A1);
};
Of course, this needs to be extended for the number of arguments you want to support, but that is done once and then buried in a "get_signature.h" header. :)
Another option I consider but immediatly discarded was SFINAE, which would introduce even more boilerplate code than the TMP version.
So, yeah, that are the options that I know of. Hope one of them works for you. :)
I've seen this question come up one too many times. libc++ now compiles this code without ambiguity (as a conforming extension).
Overdue Update
This "extension" proved sufficiently popular that it was standardized in C++14 (though I was not personally responsible for getting that job done).
In hindsight, I did not get this extension exactly correct. Earlier this month (2015-05-09) the committee voted in LWG issue 2420 which effectively changes the definition of Callable so that if the std::function has a void return type it will ignore the return type of the wrapped functor, but still otherwise consider it Callable if everything else matches up, instead of considering it not Callable.
This post-C++14 tweak does not impact this particular example since the return types involved are consistently int.
Here's an example of how to wrap std::function in a class that checks invokability of its constructor parameters:
template<typename> struct check_function;
template<typename R, typename... Args>
struct check_function<R(Args...)>: public std::function<R(Args...)> {
template<typename T,
class = typename std::enable_if<
std::is_same<R, void>::value
|| std::is_convertible<
decltype(std::declval<T>()(std::declval<Args>()...)),
R>::value>::type>
check_function(T &&t): std::function<R(Args...)>(std::forward<T>(t)) { }
};
Use like this:
int a(check_function<int ()> f) { return f(); }
int a(check_function<int (int)> f) { return f(0); }
int x() { return 22; }
int y(int) { return 44; }
int main() {
a(x);
a(y);
}
Note that this isn't quite the same as overloading on function signature, as it treats convertible argument (and return) types as equivalent. For exact overloading, this should work:
template<typename> struct check_function_exact;
template<typename R, typename... Args>
struct check_function_exact<R(Args...)>: public std::function<R(Args...)> {
template<typename T,
class = typename std::enable_if<
std::is_convertible<T, R(*)(Args...)>::value>::type>
check_function_exact(T &&t): std::function<R(Args...)>(std::forward<T>(t)) { }
};
std::function<T> has a conversion ctor that takes an arbitrary type (i.e., something other than a T). Sure, in this case, that ctor would result in a type mismatch error, but the compiler doesn't get that far -- the call is ambiguous simply because the ctor exists.