Why do objects returned from bind ignore extra arguments?
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.
Is std::bind ignoring superfluous arguments standard-conforming?
Yes, for std::bind
, the superfluous arguments will be discarded.
If some of the arguments that are supplied in the call to g() are not matched by any placeholders stored in g, the unused arguments are evaluated and discarded.
Why std::bind can be assigned to argument-mismatched std::function?
That is correct behavior.
std::bind
needs this looseness to fit its own specification.
Consider std::placeholders
, which is used to mark parameters that are passed through to the bound function.
using std::placeholders;
std::function<void(int)> f2 = std::bind( F, _1 );
// Parameter 1 is passed to ^^
// the bound function.
f2(7); // The int 7 is passed on to F
Similarly, there is _2
for the second parameter, _3
for the third, and so on.
That brings up an interesting question. How should this function object behave?
auto f3 = std::bind( F, _3 );
As you might imagine, it follows its own promise to pass the third parameter to F. Which means it does nothing to the first two parameters.
f3(10, 20, 30); // The int 30 is passed on to F. The rest? Ignored.
So this is expected behavior, and possibly the only "feature" that std::bind
holds over lambdas, even in C++14 and C++17.
The object produced by std::bind
is designed to accept and ignore any extraneous parameters.
std::bind assigned to std::function
Note: this answer assumes that you have read Why do objects returned from bind ignore extra arguments?
The object returned by std::bind
is not a std::function
; it is an object of an unspecified type that differs from std::function
in a number of ways; most importantly for your case, it ignores any extra arguments passed to it.
If you use placeholders up to _2
(for example) in your bind expression, the returned object can be called with any number of arguments as long as that number is at least 2.
- What is auto doing that the compiler doesn't complain when I call jj_2a(5, 6)? That function has all its parameters bound.
You are passing extra arguments; those extra arguments are ignored.
- But if I don't use auto, I get the behaviour I expect (compile error with arguments). So clearly function is not at all what auto decided.
Correct; bind
does not return a std::function
, it returns a callable object of unspecified type that can (depending on signature) be used to construct a std::function
.
- If I bind the first argument and not the second (jj_3), then calling with two arguments works (but drops the wrong argument, according to my mental model) while calling with one argument (which I think should work) doesn't compile.
If you use std::placeholders::_2
, you must pass at least 2 arguments. std::placeholders::_2
picks the second argument passed to the object returned from bind
.
- Using std::functional for jj_3_f says "no viable conversion", though the error message isn't so far helping me.
If you use std::placeholders::_2
, you must pass at least 2 arguments; the constructor of std::function
checks this.
Is template type of a std::function detached by std::bind an undefined behavior?
From cppreference.com:
If some of the arguments that are supplied in the call to [the bound object] are not matched by any placeholders stored in [the bound object], the unused arguments are evaluated and discarded.
The bound object from the question has no placeholders. So the nullptr
in
return filter(nullptr);
is evaluated and discarded, and the result is the same as if
return filter();
was called. If we interpret the binding, this is the same as
return filter_entity(ptr);
(or would be if ptr
was in scope). If you want to use the nullptr
in the return statement, you would need to use std::placeholders::_1
in your std::bind
.
A followup question to this might be Why do objects returned from bind ignore extra arguments?
c++: How to write a std::bind-like object that checks for superfulous parameters?
Note: Restricting the number of arguments breaks a feature of binders that is illustrated here.
My solution is based on counting the passed placeholders determining the greatest placeholder used. Thanks to Xeo for pointing out this error.
#include <functional>
#include <type_traits>
#include <utility>
template<class T, class U>
constexpr auto c_max(T&& t, U&& u)
-> typename std::remove_reference<decltype( t > u ? t : u )>::type
{ return t > u ? t : u; }
template<class...>
struct max_placeholder : std::integral_constant<int, 0> {};
template<class T, class... Rest>
struct max_placeholder<T, Rest...>
: std::integral_constant<int, c_max(std::is_placeholder<T>::value,
max_placeholder<Rest...>::value)>
{};
This lays the burden to correctly count the number on the user of the binder. For some bound Callables such as function pointers, it is possible to deduce the number of arguments (this also allows automatically supplying the required amount of placeholders). Once you have fixed the number of arguments either way, it's easy to write a wrapper that stores a binder and provides an operator()
template that checks the number of arguments:
template<class T, int N>
struct strict_binder
{
T binder;
template<class... Args>
auto operator()(Args&&... args)
-> decltype( binder(std::forward<Args>(args)...) )
{
static_assert(sizeof...(args) == N, "wrong number of arguments");
return binder(std::forward<Args>(args)...);
}
};
It is also possible to produce a substitution failure instead of an error.
As the strict_binder
is a binder
, you can express this concept via a partial specialization:
namespace std
{
template<class T, int N>
struct is_bind_expression< strict_binder<T, N> >
: public true_type
{};
}
All that remains is to write a function template that produces strict_binder
s. Here's a version that's similar to std::bind
:
template<class F, class... Args>
auto strict_bind(F&& f, Args&&... args)
-> strict_binder<
typename std::decay<
decltype( std::bind(std::forward<F>(f), std::forward<Args>(args)...) )
>::type,
max_placeholder<typename std::remove_reference<Args>::type...>::value
>
{
return { std::bind(std::forward<F>(f), std::forward<Args>(args)...) };
}
Essentially, the return type is a
strict_binder<decltype(std::bind(f, args...)), count_placeholders<Args...>::value>
That is, the strict_binder
stores the resulting type of std::bind
.
You can also write an apply
-like function that calls the bound function when no placeholders have been passed:
template<int N, class F, class... Args>
auto strict_bind_or_call(std::integral_constant<int, N>, F&& f, Args&&... args)
-> strict_binder<
typename std::decay<
decltype( std::bind(std::forward<F>(f), std::forward<Args>(args)...) )
>::type,
N
>
{
return { std::bind( std::forward<F>(f), std::forward<Args>(args)... ) };
}
template<class F, class... Args>
auto strict_bind_or_call(std::integral_constant<int, 0>, F&& f, Args&&... args)
-> decltype( std::bind( std::forward<F>(f), std::forward<Args>(args)... ) () )
{
return std::bind( std::forward<F>(f), std::forward<Args>(args)... ) ();
}
template<class F, class... Args>
auto strict_bind(F&& f, Args&&... args)
-> decltype( strict_bind_or_call( std::integral_constant<int, max_placeholder<typename std::remove_reference<Args>::type...>::value>{},
std::forward<F>(f), std::forward<Args>(args)... ) )
{
using max_placeholder_here =
max_placeholder<typename std::remove_reference<Args>::type...>;
return strict_bind_or_call( max_placeholder_here{},
std::forward<F>(f), std::forward<Args>(args)... );
}
This uses tag dispatch to either return a binder or the result of calling the function.
I gave up formatting this properly, you might want to introduce alias templates in a detail
namespace.
Note the decltype( std::bind(..) () )
in the second overload of strict_bind_or_call
is a simple way to reproduce the semantics of INVOKE
/ bind
; I can't just write f(args...)
because f
might be a member function.
Usage example:
#include <iostream>
void foo(int p0, int p1)
{ std::cout << "[" << p0 << ", " << p1 << "]\n"; }
int main()
{
auto f0 = strict_bind(foo, std::placeholders::_1, 42);
f0(1);
strict_bind(foo, 1, 2);
}
Can I use boost::bind to store an unrelated object?
Yes, you can do this by binding the extra parameters. I often did that with asio, e.g. in order to keep buffers or other state alive during the async operation.
You can also access these extra arguments from the handler afterwards by extending the handler signature to utilize them:
void Connect(const error_code& errorCode, boost::shared_ptr<asio::deadline_timer> timer)
{
}
timer.async_wait(
boost::bind(&Connect, boost::asio::placeholders::error, timer));
Is using boost::bind to pass more arguments than expected safe?
Yes, this is safe and portable – as mentioned explicitly in the documentation, the bind-expression returned by boost::bind
silently ignores extra arguments.
I.e., in your first example, func
does not receive the values 1
, 2
, and 3
– boundFunc
receives the values 1
, 2
, and 3
and forwards them to the contained bind-expression, which safely receives and ignores them, and then invokes func()
. Likewise, in your second example, zeroArg
receives the value 42
and forwards it to the contained bind-expression, which receives and ignores the value and then calls func0()
.
Chaining apply to bind. Why do I need to pad my array with 1 extra value?
The first argument that bind
accepts is the this
value to be used inside the function. So, if you use
fn.bind.apply(fn, arr)
the first item of the arr
becomes the first argument of bind, eg, it's equivalent to:
fn.bind(arr[0], arr[1], arr[2] /* ... */ )
So the first item of the array becomes the this
value, the second item of the array becomes the first argument, the third item of the array becomes the second argument, etc.
Here's a live example of how "padding" the array with a value at the start becomes the new this
value when the function is called:
const obj = { prop: 'val' };
function foo(string, num) { console.log("calling foo ---", string, num); console.log('this:', this);}
const fn = foo.bind.apply(foo, [obj, 'str', 55]);fn();
How to pass only second argument of a bind function?
Easier to use a lambda for your problem I think:
function<void(int, double)> onSetMin = [this](int dummy, double d) { SetMin(d); };
mEventHandler.on(kParamID, onSetMin);
Related Topics
What Encoding Does Std::String.C_Str() Use
How Computer Does Floating Point Arithmetic
How to Call Derived Class Method from Base Class Pointer
Portable Zip Library for C/C++ (Not an Application)
How to Find the Current System Timezone
How to Read from a Text File, Character by Character in C++
Why Doesn't Delete Destroy Anything
Function to Mangle/Demangle Functions
How to Pass Command Line Arguments to a C Program
How to Do Input Validation in C++ with Cin
C++ Boost: What's the Cause of This Warning
Std::Map Default Value for Build-In Type
How to Know If a Type Is a Specialization of Std::Vector
Call Destructor and Then Constructor (Resetting an Object)