Overloading std::function argument to match lambda [duplicate] - c++

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Disambiguating calls to functions taking std::functions
Isn't the template argument (the signature) of std::function part of its type?
I want to overload a function so that it can be called with a variety of different lambdas (generally with more or fewer arguments) naturally. The obvious thing I tried was:
#include <functional>
#include <iostream>
extern void fn(std::function<void(int)>);
extern void fn(std::function<void(int, int)>);
void test()
{
fn([](int a) { std::cout << "lambda with 1 arg " << a << std::endl; });
}
However, this fails with g++ (tried v4.6.2 and v4.7.1) with the error:
test.cc: In function ‘void test()’:
test.cc:9:74: error: call of overloaded ‘fn(test()::<lambda(int)>)’ is ambiguous
test.cc:9:74: note: candidates are:
test.cc:4:13: note: void fn(std::function<void(int)>)
test.cc:5:13: note: void fn(std::function<void(int, int)>)
Now I found an alternate (and much more complex) approaches here and here, but my question is, why does the above code fail? Is there something in the standard that says it can't work, or is this merely a bug/limitation of g++?

Every Lambda [](int a) { std::cout << "lambda with 1 arg " << a << std::endl; } has unique type even another lambda same as above will result in different lambda type with member operator()(int a)
Your implementation of std::function has a templated conversion that can be used by both std::function<void(int)> and std::function<void(int, int)>. While only one of them compiles when instantiated, they're both considered for overload resolution, and that's what creates the ambiguity. To get the desired result the library needs to employ SFINAE to exclude the erroneous one from the overload candidate set (recent versions of libc++ do that).

The question sounds inside-out. You define a type using std::function to describe how you're going to call objects of that type and what their return value is. You can then use that specialization of std::function to wrap various callable objects, including lambdas, that have different argument types or a different return type.

Related

std::function fails to distinguish overloaded functions

I am trying to understand why std::function is not able to distinguish between overloaded functions.
#include <functional>
void add(int,int){}
class A {};
void add (A, A){}
int main(){
std::function <void(int, int)> func = add;
}
In the code shown above, function<void(int, int)> can match only one of these functions and yet it fails. Why is this so? I know I can work around this by using a lambda or a function pointer to the actual function and then storing the function pointer in function. But why does this fail? Isn't the context clear on which function I want to be chosen? Please help me understand why this fails as I am not able to understand why template matching fails in this case.
The compiler errors that I get on clang for this are as follows:
test.cpp:10:33: error: no viable conversion from '<overloaded function type>' to
'std::function<void (int, int)>'
std::function <void(int, int)> func = add;
^ ~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1266:31: note:
candidate constructor not viable: no overload of 'add' matching
'std::__1::nullptr_t' for 1st argument
_LIBCPP_INLINE_VISIBILITY function(nullptr_t) : __f_(0) {}
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1267:5: note:
candidate constructor not viable: no overload of 'add' matching 'const
std::__1::function<void (int, int)> &' for 1st argument
function(const function&);
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__functional_03:1269:7: note:
candidate template ignored: couldn't infer template argument '_Fp'
function(_Fp,
^
1 error generated.
EDIT - In addition to MSalters' answer, I did some searching on this forum and found the exact reason why this fails. I got the answer from Nawaz's reply in this post.
I have copy pasted from his answer here:
int test(const std::string&) {
return 0;
}
int test(const std::string*) {
return 0;
}
typedef int (*funtype)(const std::string&);
funtype fun = test; //no cast required now!
std::function<int(const std::string&)> func = fun; //no cast!
So why std::function<int(const std::string&)> does not work the way funtype fun = test works above?
Well the answer is, because std::function can be initialized with any object, as its constructor is templatized which is independent of the template argument you passed to std::function.
It's obvious to us which function you intend to be chosen, but the compiler has to follow the rules of C++ not use clever leaps of logic (or even not so clever ones, as in simple cases like this!)
The relevant constructor of std::function is:
template<class F> function(F f);
which is a template that accepts any type.
The C++14 standard does constrain the template (since LWG DR 2132) so that it:
shall not participate in overload resolution unless f is Callable (20.9.12.2) for argument types ArgTypes... and return type R.
which means that the compiler will only allow the constructor to be called when Functor is compatible with the call signature of the std::function (which is void(int, int) in your example). In theory that should mean that void add(A, A) is not a viable argument and so "obviously" you intended to use void add(int, int).
However, the compiler can't test the "f is Callable for argument types ..." constraint until it knows the type of f, which means it needs to have already disambiguated between void add(int, int) and void add(A, A) before it can apply the constraint that would allow it to reject one of those functions!
So there's a chicken and egg situation, which unfortunately means that you need to help the compiler out by specifying exactly which overload of add you want to use, and then the compiler can apply the constraint and (rather redundantly) decide that it is an acceptable argument for the constructor.
It is conceivable that we could change C++ so that in cases like this all the overloaded functions are tested against the constraint (so we don't need to know which one to test before testing it) and if only one is viable then use that one, but that's not how C++ works.
While it's obvious what you want, the problem is that std::function cannot influence overload resolution of &add. If you were to initialize a raw function pointer (void (*func)(int,int) = &add), it does work. That's because function pointer initialization is a context in which overload resolution is done. The target type is exactly known. But std::function will take almost any argument that's callable. That flexibility in accepting arguments does mean that you can't do overload resolution on &add. Multiple overloads of add might be suitable.
An explicit cast will work, i.e. static_cast<void(*)(int, int)> (&add).
This can be wrapped in a template<typename F> std::function<F> make_function(F*) which would allow you to write auto func = make_function<int(int,int)> (&add)
Try:
std::function <void(int, int)> func = static_cast<void(*)(int, int)> (add);
Addresses to void add(A, A) and void add(int, int) obvoiusly differes. When you point to the function by name it is pretty much imposible for compiler to know which function address do you need. void(int, int) here is not a hint.
Another way to deal with this is with a generic lambda in C++14:
int main() {
std::function<void(int, int)> func = [](auto &&... args) {
add(std::forward<decltype(args)>(args)...);
};
}
That will create a lambda function that will resolve things with no ambiguity.
I did not forward arguments,
As far as I can see, it's a Visual Studio problem.
c++11 standard (20.8.11)
std::function synopsis
template<class R, class... ArgTypes> class function<R(ArgTypes...)>;
but VisualStudio doesn't have that specialization
clang++ and g++ are perfectly fine with overloading std::functions
prior answers explain why VS doesn't work, but they didn't mention that it's VS' bug

Function overload using lambda function signature

Consider the following example
void foo(const std::function<int()>& f) {
std::cout << f() << std::endl;
}
void foo(const std::function<int(int x)>& f) {
std::cout << f(5) << std::endl;
}
int main() {
foo([](){return 3;});
foo([](int x){return x;});
}
This does not compile, because the call to foo is said to be ambiguous. As far as I understand this is due to the fact, that the lambda function is not a priori a std::function but has to be casted to it and that there is a std::function constructor that takes an arbitrary argument.
Maybe someone can explain to me why anyone would create an implicit constructor that takes an arbitrary argument. However my acutual question is whether there is a workaround, which allows to use the function signature of lambda functions to overload a the function foo. I have tried function pointers, but that didn't work because capturing lambda functions can't be cast to a normal function pointer.
Any help is most welcome.
Your compiler is correct according to C++11. In C++14, a rule is added that says that the constructor template shall not participate in overload resolution unless the type of the argument is actually callable with the std::function's argument types. Therefore, this code is supposed to compile in C++14, but not in C++11. Consider this to be an oversight in C++11.
For now, you can work around this by explicit conversion:
foo(std::function<int()>([](){return 3;}));
http://coliru.stacked-crooked.com/a/26bd4c7e9b88bbd0
An alternative to using std::function is to use templates. Templates avoid the memory allocation overhead associated with std::function. The template type deduction machinery will deduce the correct type of the lambda passed so the call site cast goes away. However you still have is disambiguate the overloads for the no-args vs args case.
You can do this using a trick with trailing return types that behave similar to enable_if.
template<typename Callable>
auto baz(Callable c)
-> decltype(c(5), void())
{
std::cout << c(5) << std::endl;
}
The above overload of baz will only be a valid overload candidate when the template parameter Callable can be called with an argument of 5.
You can put more advanced mechanisms on top of this to make it more general (ie. variadic pack expansion of args into callable) but I wanted to show the basic mechanism working.

Understanding how the compiler resolves pointer to functions

The following code snippet:
#include <iostream>
void does() { std::cout << "do" << std::endl; }
void does(bool b = false) { std::cout << "do(bool)" << std::endl; }
void fwd(void (*func)(bool))
{
func(false);
}
int main(int, char**)
{
fwd(&does);
fwd(does);
fwd(*does);
}
understandably produces the following error:
test.cpp:15:10: error: overloaded function with no contextual type information
The compiler cannot discern which of the functions I intend to use.
What I don't understand is why the code will correctly run when I comment out the line that reads:
fwd(*does)
Why can the compiler suddenly resolve the ambiguousness?
int main(int, char**)
{
fwd(&does);
fwd(does);
}
Also, without overloading does the snippet will correctly run with all 3 calls.
This snippet runs fine...
#include <iostream>
void does(bool b = false) { std::cout << "do(bool)" << std::endl; }
void fwd(void (*func)(bool))
{
func(false);
}
int main(int, char**)
{
fwd(&does);
fwd(does);
fwd(*does);
}
I'm compiling this with gcc 4.6.3 on a Linux box.
Thanks for the help!
The answer to your question is in the overload-resolution rules for functions.
Specifically, there is an exception for using & before the function-name (once) not breaking overload-resolution, but none for using *.
Also see that only one of those two functions accept that single argument:
13.4 Address of overloaded function [over.over]
1 A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. The function selected is the one whose type is identical to the function type of the target type required in the context. [ Note: That is, the class of which the function is a member is ignored when matching a pointer-to-member-function type. —end note ] The target can be
an object or reference being initialized (8.5, 8.5.3),
the left side of an assignment (5.17),
a parameter of a function (5.2.2),
a parameter of a user-defined operator (13.5),
the return value of a function, operator function, or conversion (6.6.3),
an explicit type conversion (5.2.3, 5.2.9, 5.4), or
a non-type template-parameter (14.3.2).
The overloaded function name can be preceded by the & operator. An overloaded function name shall not be used without arguments in contexts other than those listed.
Quote is from n3242 (c++11), with bold by me.
fwd expects a function that takes a boolean parameter; you only have one such version of does, so there is no confusion. In effect, does and &does are considered the same (because functions cannot be values, one of these two should technically be incorrect if not impossible to represent, but the language has to chosen to instead just treat them as the same thing).
But when you try to use fwd(*does), you would need a definition of does that dereferences to such a function, and you don't have anything like that -- in fact, as I have been recently schooled, you can't have anything like that.

Template function causes a compiler error when used with local lambda

My previous question concluded that a distasteful "double cast" might be necessary to use the POSIX makecontext with a C++ lambda function (i.e. function object). Moving on, I'm now faced with a compilation error relating to the following minimal code:
#include <iostream>
#include <ucontext.h>
using namespace std;
template <typename T> void foo() {
ucontext_t c;
auto f = [=](int i){ cout << i << endl; };
makecontext(&c, (void (*) (void)) (void (*)(int)) f, 1, 12345);
}
int main(int argc, char *argv[]) {
foo<int>();
return 0;
}
The error is:
error: invalid cast from type ‘foo() [with T = int]::<lambda(int)>’ to type ‘void (*)(int)’
However, if I remove the unused (in this example) template argument from the foo function, so it becomes void foo();, and change the call to foo() the error disappears. Could someone tell me why? I'm using G++ 4.6.
Edit:
From the comments below, it seems the [=] in the code above causes the lambda to be a "capturing" lambda, regardless of the fact that it doesn't actually capture anything. The [=] is not necessary in my code, alas replacing with [] in GCC 4.6 does not remove the error. I am installing GCC 4.6.1 now...
If you use [=] to induce your lambda, you will not get a function pointer (or an object that is convertible to one). You will get a function object. And no amount of casting is going to allow you to pass that to makecontext. Not in any way that actually works.
According to N3291, the most recent working draft of C++0x:
The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.
This is the only place where the specification allows conversion to a function pointer. Therefore, if recent versions of GCC do allow conversion to function pointers for [=], that not in accord with the specification.
Only captureless lambdas are convertible to function pointers; while f technically does not capture anything, it does have a default capture mode of capturing by value (for no apparent reason).
Change [=] to [] in the declaration of f and it should work as expected.
EDIT: The fact that this compiles with more recent versions of GCC (as noted by Kerrek) gives a strong indication that this is merely a compiler bug in the version you're using.

Using std::tr1::bind with std::vector::push_back

Why my VS2010 can't compile this code:
#include <functional>
#include <vector>
int main()
{
std::vector<int> vec;
std::bind(&std::vector<int>::push_back, std::ref(vec), 1)();
return 0;
}
You should be more specific why this doesn't seem to work for you.
#include <iostream>
#include <tr1/functional>
#include <vector>
int main(int argc, char* argv[]) {
std::vector<int> vec;
std::tr1::bind(&std::vector<int>::push_back, std::tr1::ref(vec), 1)();
std::cout << "vec.size = " << vec.size() << std::endl;
std::cout << "vec[0] = " << vec[0] << std::endl;
return 0;
}
$ gcc -o test -lstdc++ test.cpp && ./test
vec.size = 1
vec[0] = 1
Update: Luc Danton is right, the issue here is the overloaded push_back. See question Are there boost::bind issues with VS2010 ?. Also, note that the issue is not limited to push_back, see Visual Studio 2010 and boost::bind.
The bottom line is that what you're trying to do isn't possible in portable C++. std::vector<>::push_back is guaranteed to be overloaded in C++11 compilers, as at a minimum there must be an overload for lvalues and an overload for rvalues.
Usually, when taking the address of an overloaded member function, §13.4/1 in the C++11 FDIS tells us that we can control which overload we're taking the address of thusly:
A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. The function selected is the one whose type is identical to the function type of the target type required in the context. [ Note: That is, the class of which the function is a member is ignored when matching a pointer-to-member-function type. —end note ] The target can be
an object or reference being initialized,
the left side of an assignment,
a parameter of a function,
a parameter of a user-defined operator,
the return value of a function, operator function, or conversion,
an explicit type conversion, or
a non-type template-parameter.
The overloaded function name can be preceded by the & operator. An overloaded function name shall not be used without arguments in contexts other than those listed. [ Note: Any redundant set of parentheses surrounding the overloaded function name is ignored. —end note ]
The problem comes from §17.6.5.5/2:
An implementation may declare additional non-virtual member function signatures within a class by adding arguments with default values to a member function signature; hence, the address of a member function of a class in the C++ standard library has an unspecified type.
Consequently, it is not portable to ever take the address of a standard library class member function, as the type of such an expression is by definition unknowable except on an implementation-by-implementation basis.
Luc Danton's proposed workaround (specifically, using a lambda) is also what I would recommend:
std::vector<int> vec;
[&](){ vec.push_back(1); }();
Try this:
struct push_back {
void
operator()(std::vector<int>& vector, int i) const
{
vector.push_back(i);
}
};
// somewhere else:
std::vector<int> vec;
std::tr1::bind(push_back(), std::tr1::ref(vec), 1)();
With C++03 note that push_back cannot be a local type; with C++11 it can but it would be more idiomatic (and completely equivalent) to use a lambda.
In all likeliness your implementation provides overloads for std::vector<T>::push_back and thus its address would have to be disambiguated. If this is what happened, your compiler should have provided you with an appropriate error message. In all cases, you should explain what you mean by "it's not possible".
The point is not to use such helper
functions. – magenta
Then why didn't you put it in the question? I can't read your mind.
You can also try this:
std::vector<int> vec;
void (std::vector<int>::*push_back)(int const&) = &std::vector<int>::push_back;
std::tr1::bind(push_back(), std::tr1::ref(vec), 1)();
Which I believe is not guaranteed to success.
It should propably lok like this:
std::vector<int> vec;
std::tr1::bind(&std::vector<int>::push_back, std::tr1::ref(vec), _1)(1);