22

Suppose I have a function that takes two arguments,

void f(int x, int y);

and I want to bind one of them. I can use std::bind as follows:

auto partiallyBoundF = std::bind(f, 10, _1);

partiallyBoundF takes only one argument, but I can call it with more than one. The arguments beyond the first don't even have to be of a type that makes any sense:

partiallyBoundF(20, 0);
partiallyBoundF(0, 44, -99, "Hello", 4.5, true, []{});

What is the purpose of permitting objects returned from bind to be passed extra arguments? It allows calling errors to compile that would be rejected anyplace else.

KnowItAllWannabe
  • 12,972
  • 8
  • 50
  • 91
  • 3
    What compiler? I would guess this may be just a non-conforming compiler (because allowing those additional arguments really makes no sense and I doubt the standard allows this). For example MSVC emulates variadic templates by just defining each variadic template to take the maximum possible template arguments and defaulting those to some NIL-type. Maybe something like that is the cause for your behaviour? – Christian Rau Nov 06 '12 at 13:30
  • 3
    @ChristianRau: It's part of the standard. It was part of TR1, too. 20.8.2/4 makes the comment that the expected implementation is for a variadic-templatized `operator()` to simply take whatever is passed to it, regardless of type or number of arguments. TR1 has similar wording. – KnowItAllWannabe Nov 06 '12 at 13:44
  • 1
    In your case, f(w1, ..., wN) where `N = sizeof...(bound_args)` (number of args to bind call) shall be a valid expression, see 20.8.9.1.2/2 and 20.8.2/1. Edit: It does NOT disallow any other way to call it. – dyp Nov 06 '12 at 13:45
  • 1
    @KnowItAllWannabe It allows to take an "arbitrary argument list", but "delivers the arguments to the wrapped callable object". I think it's arguable whether that allows this kind of calls (after all, `f(int, double, whatever)` is not valid). – dyp Nov 06 '12 at 14:00
  • @ChristianRau: MSVC11 with the November CTP accepts extra arguments without complaint. – KnowItAllWannabe Nov 06 '12 at 15:42
  • @KnowItAllWannabe: Yes, and Microsoft made it very clear that while the November CTP does include varadic templates, they have *not yet updated* the standard library to *use them*. I don't understand how it is people keep getting this wrong; they emphasized this *constantly* when they talk about the CTP. – Nicol Bolas Nov 06 '12 at 17:04

1 Answers1

19

Ignoring extra arguments is a lot simpler to implement, and can actually be useful.

In a typical implementation e.g. libstdc++ (g++), the approach taken is to collect the operator() arguments into a tuple and then let the std::placeholder bind arguments extract them as required. Enforcing argument count would require counting the number of used placeholders, which would be pretty complicated. Note that the bind callable can be a functor with multiple or templated operator() call patterns, so the bind object operator() can't be generated with a single "correct" signature.

Also note that you can write:

std::bind(&foo, std::placeholders::_1, std::placeholders::_3);

i.e. explicitly ignoring the second argument to the bind object. If bind enforced its argument count you would need an additional way to specify that e.g. a fourth argument was also to be ignored.

As for usefulness, consider binding a member signal handler to a signal:

sig.connect(std::bind(&C::on_sig, this, param, std::placeholders::_1));

If sig has extra unwanted emission parameters, then they are simply ignored by the bind object; otherwise, binding the same handler to multiple signals would require writing multiple forwarding wrappers for no real purpose.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • 3
    “more useful” – I’d debate that. This breaks strong typing. And if desired, this behaviour could still be emulated with a strict `std::bind` simply by using a proxy functor which accepts variadic arguments. – Konrad Rudolph Nov 06 '12 at 14:03
  • 2
    @KonradRudolph a bind expression doesn't *have* a well-defined type in the general case, if its callable is a functor with multiple `operator()`s. – ecatmur Nov 06 '12 at 14:08
  • You can ignore a fourth argument using a lambda. Not very nice, though. – dyp Nov 06 '12 at 14:13
  • 3
    @KonradRudolph in fact, the set of valid calling signatures of a `bind` expression is *non-computable* in the general case, by obvious construction, even for a strict `std::bind`. – ecatmur Nov 06 '12 at 14:20