40

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.

Xeo
  • 123,374
  • 44
  • 277
  • 381
GhostlyGhost
  • 493
  • 5
  • 5

4 Answers4

42

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. :)

Xeo
  • 123,374
  • 44
  • 277
  • 381
  • 11
    @nightcracker: Err... you hate C++ because it's so powerful? :x – Xeo Jan 16 '12 at 21:52
  • 1
    No, I hate C++ because of the shitload of boilerplate and hacks like SFINEA. The way C++ implements OOP in a statically typed language is such a productivity drain. Now I'll be honest with you, I don't grok C++ like I do Python, and ofcourse most things take longer to make in a statically typed, compiled language, but C++ is just pushing it. I mean, just look at the Boost source code... ` ;)` – orlp Jan 17 '12 at 13:31
  • 12
    @nightcracker: has it occurred to you that your problem might be the assumption that C++ is an OOP language? I fail to see how you can criticize C++ for being too verbose when you ask it to do something *that isn't possible at all in other languages*. If other languages allowed you to use SFINAE, then sure, you could argue that C++'s version of it was too verbose. – jalf Jan 17 '12 at 14:00
  • 2
    @jalf: I never said C++ was an OOP language, I was criticizing the way C++ implements OOP. Sadly, I know no other language than C++ that does what it does. And TBH, I wouldn't know a better way as we speak. All I know is that I really dislike the way C++ does it. Also, I don't only find verbosity a problem (verbosity is good, usually), but also semantics. To give an example, the `get_signature` struct has no semantic meaning to me, whatsoever - this makes it a hack for me. But discussing this here won't help anyone, if you really want to reply to this, suggest some chat and I will try to join. – orlp Jan 17 '12 at 14:16
  • 6
    @nightcracker: TBH, I know no other way to criticize C++, but I nevertheless hate the way you do it. – sbi Jan 17 '12 at 14:27
  • 1
    A fourth option is to wrap `std::function` in a class that does check its constructor callable parameter: http://stackoverflow.com/a/12054513/567292 – ecatmur Aug 21 '12 at 12:09
11

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.

Howard Hinnant
  • 179,402
  • 46
  • 391
  • 527
  • Nice, SFINAE'd I take? (Can't seem to find the `` head in the SVN repo on-site.) – Xeo Jun 01 '11 at 00:57
  • Yes, with SFINAE (the world's most unpronounceable acronym). Here's : http://llvm.org/svn/llvm-project/libcxx/trunk/include/functional – Howard Hinnant Jun 01 '11 at 02:38
  • I like the way Stephan Lavavej pronounces it. :) "esfiney". Also, SBRM. ;) – Xeo Jun 01 '11 at 02:40
  • Wow, I took a look at (the deeply hidden) `__invokable` SFINAE construct in [``](http://llvm.org/svn/llvm-project/libcxx/trunk/include/type_traits) .. just how the hell does the `__invoke` part of it work? I can understand the version under `// bullet 5`, for functors and free / static functions. The other seem to be for member functions, but how does that meta function differentiate between the 5 versions? Especially, how does it choose between 1 & 2 and 3 & 4 respectively, as they look exactly the same too me w.r.t. the signature. – Xeo Jun 01 '11 at 03:15
  • Quite honestly, I'm not sure how this works. I was pretty amazed when my code evolved into this and was still passing my tests. The code started out a **lot** more complicated. This is a brand new language and I have yet to master it. My limited understanding is that the trailing return type **is** the SFINAE trigger. If that expression is valid, you've got an overload, else you don't. I'm hoping this is valid C++11, and that I'm not accidentally taking advantage of a clang bug or extension. – Howard Hinnant Jun 01 '11 at 12:23
  • This would make SFINAE quite a lot easier for such things, I hope it really is valid in C++0x/11. :) Btw, what is `__nat`? "not a type"? – Xeo Jun 01 '11 at 12:53
  • Just had time to play with the idea of the trailing return type as the SFINAE trigger, but it neither MSVC (no surprise), not GCC would let me compile. :( It always errored out on the overloads. See these tests, maybe I made a mistake: http://ideone.com/MUhxm. It's basically a copy&paste from the `` header I linked to in an earlier comment, just added a `main` function and changed the _STD thingy. – Xeo Jun 03 '11 at 08:24
  • I copy/pasted your code into a clang++ test. It compiled and output `can invoke Foo with int`. I do not know what the status is on this part of the std for gcc and msvc. What gcc version are you using? – Howard Hinnant Jun 03 '11 at 13:33
  • Used Ideone for this one, so 4.5.1. – Xeo Jun 03 '11 at 13:35
  • That may predate the introduction the "expanded SFINAE" rules, but I'm not positive. – Howard Hinnant Jun 03 '11 at 13:39
  • "neither MsVC (...) would let me compile". That's quite misleading. Of course, MSVC can't compile the variadic template stuff, but if you manually generate for a few args then it compiles fine (for example, copy this http://ideone.com/zHsdE in MSVC). Function-objects are correctly detected by __invokable. I don't know how to test bullet 1/2 and bullet 3/4 though. – Thomas Petit Jun 04 '11 at 11:39
  • @Thomas: Of course the variadic template stuff wasn't in for MSVC, I threw [this little test at it](http://ideone.com/EoEig). "error C2893: Failed to specialize function template ''unknown-type' invoke(F &&,A &&)'". – Xeo Jun 05 '11 at 03:26
  • There is a missing std::forward in bullet 5 of your attempt. Your declval is also slightly different than the FDIS one. But I repeat, a correct implementation of __invokable for MSVC10 can be found here : http://ideone.com/zHsdE, compile and work at least for function-object. – Thomas Petit Jun 05 '11 at 09:04
  • @Thomas: I'm an idiot, thank you. I made a slight typo in bullet 5 where I tried to forward the functor as the argument.. of course that fails, d'oh. I also missed the invokable(any) case, but that was unimportant. – Xeo Jun 05 '11 at 09:21
  • @Thomas: Anyways, I can't seem to get the member function / data ones to work. Did you? Also, please include a short "@Xeo" at the beginning of your comment so I get notified of your reply. :) It was by pure luck that I noticed your last one. – Xeo Jun 05 '11 at 09:35
  • @Xeo: Damn, I registered more than one year ago on SO and yet never noticed the "responses" feature. Thanks ! On topic, I can't get member function to work with MSVC either:( So, it's still not really clear if libc++ takes advantage of clang peculiarity or if it's legal C++11. I hope it's the latter... – Thomas Petit Jun 05 '11 at 10:28
  • Browsing through the list of C++11 features Clang has implemented, I found [this paper](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html), which extends SFINAE to expressions in return types. Taking a look at the example at the end of the paper, it's clear that you do not just take advantage of any fluke or bug of Clang with your `__invokable` implementation. :) And the C++ coders got a reason to celebrate, for their was a new type of SFINAE! – Xeo Nov 30 '11 at 18:07
4

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)) { }
};
ecatmur
  • 137,771
  • 23
  • 263
  • 343
  • Note that your `t` is not a universal reference, therefore `std::forward` will always move it. – Xeo Aug 21 '12 at 12:21
  • And I'd use `is_convertible` instead of `is_same`, then you don't need the `remove_cv`. You should also account for `void(Args...)`, which just swallows the returned value. – Xeo Aug 21 '12 at 12:41
  • @Xeo thanks, that's more consistent. As far as I can tell my code works fine with `void` return, though. – ecatmur Aug 21 '12 at 13:05
  • 2
    If `decltype(std::declval()(std::declval()...))` evaluates to something other than `void` and `R` *is* `void`, `std::convertible::value` will yield `false`, though `std::function` allows that to swallow the return value. – Xeo Aug 21 '12 at 13:25
  • `check_function` on a function returning `void` does not check that `T` is invokable with `Args...`. Also note that by strict reading of the standard, a `std::function` cannot be safely constructed from `int foo(int){return 3;}`, so if we want that behavior, we need to lambda-shield the `std::function` from the callable. – Yakk - Adam Nevraumont Aug 19 '14 at 20:23
  • @Yakk actually, by strict reading `std::function` cannot be safely constructed at all, because even an expression of type `void` is not implicitly convertible to type `void` ([conv]/3). I've raised this on the std-discussion list but haven't pursued it further: https://groups.google.com/a/isocpp.org/d/msg/std-discussion/S9xdBpKpZMg/R3SL85z_e0AJ – ecatmur Aug 20 '14 at 09:16
  • @ecatmur I reached the [same conclusion](http://stackoverflow.com/questions/25395605/is-it-illegal-to-invoke-a-stdfunctionvoidargs-under-the-standard) earlier last night. You can construct it with `nullptr`! – Yakk - Adam Nevraumont Aug 20 '14 at 10:53
2

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.

Josh
  • 992
  • 5
  • 5
  • 1
    The constructor doesn't result in a type mismatch error, it's in the `operator()` where it happens (okay, not directly, but in a subroutine). Try constructing a `std::function` with any function that doesn't satisfy the signature, you'll see where the error message leads you. – Xeo May 09 '11 at 00:47