[bind] Function call operator is a template?!

All this time I've been using bind quite happily without really going into the details of its implementation. Recently, however, I had to discover the hard way that the function call operator that is produced as part of the bind() return is a template! I guess most of the time one would not care if it's a template or not as long as you can call the resulting functor with the right signature. It does, however, make a difference. The difference can be seen when calling the resulting functor with wrong arguments. The error detection happens earlier in the case when functor's operator() is a concrete function as opposed to a template. And that plays bad tricks with SFINAE. You see, recently I was faced with a challenge where I had to check at compile time whether calling a template argument with a specific signature is a well-formed expression. Something similar to Eric Niebler's "can_be_called<>": http://www.boost.org/doc/libs/1_41_0/doc/html/proto/appendices.html#boost_pr... Well, this technique won't work with bind(), because the template member operator() will happily accept ANY argument during SFINAE and the error detection will happen much later, when you actually try to call the functor. I'm not going to debate whether it's right or wrong, but I'm still curious - why did it have to be a template? When bind() is called it already has all the information about the function's signature so it could construct a concrete member operator() without resorting to templates. For example bind(main) could easily determine that the signature is <int (int, char * [])> and could create a functor with a single "int operator()(int, char *[])", instead of a template. It's not like it's lambdas, where when you say _1 + _2 you literally mean parameters of any type. Thanks, Andy.

On Wed, Dec 16, 2009 at 3:56 PM, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
struct myFunction { int operator()(char c, int i){...} int operator()(char c, std::string s){...} int operator()(char c, float f){...} } auto b = bind(myFunction, 42, _1); See why the template is useful now?

Peter Dimov wrote:
Do you mean to say that there may be a type that converts to both X and Y, but X and Y don't convert between each other? Yup. There's a problem. So it looks like templates are necessary. But are unconstrained templates necessary? Couldn't we put a concept check to enable templates only if underlying function/functor is callable with the given arguments. Something to what Eric Niebler proposed in another thread? ("How to detect if f() returns void"). Thanks, Andy.
participants (4)
-
Andy Venikov
-
Mathias Gaunard
-
OvermindDL1
-
Peter Dimov