问题
std::bind
is sometimes described as "partial application". Any reasons why when all parameters of a function are bound, the function itself isn't applied?
For example, the following code prints nothing.
#include <functional>
#include <iostream>
using namespace std;
using namespace std::placeholders;
void f(int a,string b) {cout << a << b << endl;};
int main() {
bind(bind(f,1,_1),"Hi!");
return 0;
}
Is there a way to write a bind variant that can apply the function when all parameters are fixed?
--Update--
I understand from the responses now that std::bind
is not exactly partial application. So, on the second part of the question, how can I write something like std::bind but does partial application.
I know bind(bind(f,1,_1),"Hi!")()
will call the final 0-ary function and return the result value (printing 1Hi
in the example). Is it possible to do template programming to call the function call operator () in the terminal case of bind?
In other words, is it possible to write a function bind1
:
template< class R, class F, class... Args >
bind1( F f, Args... args )
, such that when std::is_placeholder<T>::value == 0
for each member of args
,
bind1()
can, in addition to what std::bind()
does, call the operator()?
回答1:
A function with no arguments is just a value in Haskell. You don't call it, you just use it. Since there are no side effects, there is no observable difference.
In OCaml there are simply no parameter-less functions, to get something like that you need to add a dummy unit argument.
Not so in C++. C++, unlike Haskell and OCaml, maintains clear difference between f
and f()
. bind
gives you the former because you can always turn it into the latter by adding ()
. You can write your own wrapper for bind
that does just that quite easily. Going the other way around would be a tad more difficult.
Here's a possible implementation of such wrapper:
#include <functional>
#include <utility>
#include <iostream>
template <typename T>
struct is_noargs_callable {
private:
typedef char(&yes)[1];
typedef char(&no)[2];
template<typename U>
static yes test(decltype((std::declval<U>())())*);
template<typename>
static no test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
template <typename T>
struct is_noargs_callable<T()> {
static const bool value = true;
};
template <typename T>
struct is_noargs_callable<T(...)> {
static const bool value = true;
};
template <typename T>
auto call_me_if_you_can(T t) -> typename std::enable_if<is_noargs_callable<T>::value, decltype(t())>::type
{
return t();
}
template <typename T>
auto call_me_if_you_can(T t) -> typename std::enable_if<!is_noargs_callable<T>::value, T>::type
{
return t;
}
template <typename... Args>
auto apply(Args&&... args) -> decltype(call_me_if_you_can(std::bind(args...))) {
return call_me_if_you_can(std::bind(args...));
}
// testing
void foo(int a, int b, int c) { std::cout << "foo(" << a << "," << b << "," << c << ")"; }
int main ()
{
using namespace std::placeholders;
std::cout << "zero : " ; apply(foo, _1, _2, _3); std::cout << " : " ; apply(foo, _1, _2, _3)(1,2,3); std::cout << std::endl;
std::cout << "one : " ; apply(foo, 1, _1, _2); std::cout << " : " ; apply(foo, 1, _1, _2)(2,3); std::cout << std::endl;
std::cout << "two : " ; apply(foo, 1, 2, _1); std::cout << " : " ; apply(foo, 1, 2, _1)(3); std::cout << std::endl;
std::cout << "three: " ; apply(foo, 1, 2, 3); std::cout << " : "; /* nothing to test here */ std::cout << std::endl;
}
However, killing the difference between f
and f()
just in this one place does not IMHO contribute to the overall consistency of C++ programming. If you don't like the distinction, kill it everywhere (or just use you a Haskell for great good).
回答2:
No sources for this, just my opinion.
The reason that wasn't done is because there is no reason to do it. If you know all of the input to the function, just call it.
And if you were doing something with templates that resulted in this, you would need to write all of the code consistently anyway. A special case here would only require a special case somewhere else.
来源:https://stackoverflow.com/questions/21247882/c11-bind-and-apply