Is the template argument (signature) of the std :: function part of its type?

Given the following code, what is the reason for the ambiguity? Can I get around this or do I have to keep (annoying) explicit ghosts?

#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 on the function a() with the parameter function<int ()> and call a(x) in its main part, compilation will correctly fail due to a mismatch of type x and the argument function<int (int)> function a() . If the compiler does not work in this case, why is there any ambiguity when two a() functions are present?

I tried VS2010 and g ++ v. 4.5. Both give me the same ambiguity.

+37
c ++ c ++ 11 std-function
May 08 '11 at 23:57
source share
4 answers

The problem is that both function<int()> and function<int(int)> are constructive from the same function. Here's what the std::function constructor declaration 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 constructive from anything.
std::/boost::function use a method called type erasure to allow the transfer of harsh objects / functions, so that they satisfy the signature for a long time when called. One of the drawbacks is that you get an error in the deepest part of the implementation (where the save function is called) when delivering an object that cannot be called, as required for its signature, and not in the constructor.




The problem can be illustrated by this small class:

 template<class Signature> class myfunc{ public: template<class Func> myfunc(Func a_func){ // ... } }; 

Now that the compiler is looking for valid functions for the overload set, it tries to convert the arguments if there is no perfect fitting function. The conversion can occur through the constructor of the function parameter or through the conversion operator of the argument specified by the function. In our case, this is the first.
The compiler is trying to perform the first overload a . To make it viable, he must make a transformation. To convert a int(*)() to myfunc<int()> , he tries to create the myfunc constructor. As a template that accepts something, the transformation naturally succeeds.
Now he is trying to do the same with the second overload. The constructor still remains the same and still takes something that is given to it, the conversion also works.
Left with two functions in the overload set, the compiler is a sad panda and does not know what to do, so it just says that the call is ambiguous.




So, at the end, the Signature part of the template is type when creating declarations / definitions, but not when you want to build an object.




Edit :
With all my attention, answering the title question, I completely forgot about your second question :(

Can I get around this or do I have to keep (annoying) explicit ghosts?

Afaik, you have 3 options.

  • Keep the cast
  • Make a function object of the appropriate type and pass it

    function<int()> fx = x; function<int(int)> fy = y; a(fx); a(fy);

  • Hide tedious casting in functions and use TMP to get the correct signature

The TMP version (metaprogramming template) is rather verbose and has boilerplate code, but hides casting from the client. An example version can be found here , which is based on the get_signature metafile, which partially specializes in types of function pointers (and gives a good example of 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 expanded for the number of arguments that you want to support, but this is done once and then buried in the header "get_signature.h" . :)

Another option that I consider, but immediately discarded, was SFINAE, which would present even more boilerplate code than the TMP version.

So yes, these are the options that I know of. Hope one of them works for you. :)

+39
May 09 '11 at
source share

I have seen this question come up too many times. libC ++ now compiles this code without ambiguity (as a corresponding extension).

Expired Update

This "extension" proved to be quite popular because it was standardized in C ++ 14 (although I personally was not responsible for the implementation of this work).

Looking back, I did not get this extension for sure. Earlier this month (2015-05-09), the committee voted on LWG issue 2420 , which effectively changes the definition of Callable so that if std::function has a return type of void , it will ignore the return type of a wrapped functor, but otherwise otherwise would consider it Callable if all the others are the same, instead of considering it not as Callable.

This setting after C ++ 14 does not affect this particular example, since the involved types are returned int sequence.

+10
May 31 '11 at 23:16
source share

Here is an example of how to wrap std::function in a class that checks the 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 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 is not exactly the same as overloading the function signature, since it treats the types of convertible arguments (and return) as equivalent. For accurate overload, 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)) { } }; 
+4
Aug 21 2018-12-12T00:
source share

std::function<T> has a ctor conversion that takes on an arbitrary type (i.e. something other than a T ). Of course, in this case ctor will lead to a type mismatch error, but the compiler will not get to this point - the call is ambiguous simply because ctor exists.

+2
May 9 '11 at a.m.
source share



All Articles