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
Related
For the below code, I get the error
#include <iostream>
#include <functional>
using namespace std;
class A {};
class B {};
namespace N
{
void func(A a, int z){}
void func(B a, int z){}
}
void func_1(std::function<void(A, int)> x)
{
A a;
x(a, 1);
}
int main()
{
func_1(N::func);
return 0;
}
Error:
main.cpp:23:19: error: cannot resolve overloaded function 'func' based on conversion to type 'std::function<void(A, int)>'
23 | func_1(N::func);
If we do the static cast for the func_1(N::func); as func_1(static_cast<void (&)(A, int)>(N::func));, then this work fine. But I would expect this to work without a cast.
std::function<void(A, int)> is more complicated than void(*)(A, int).
template< class F >
function( F f );
Initializes the target with std::move(f). If f is a null pointer to function or null pointer to member, *this will be empty after the call. This constructor does not participate in overload resolution unless f is Callable for argument types Args... and return type R.
You don't even know what constructors participate in overload resolution until you decide which N::func you mean.
One can conceive of an combined overload resolution and template argument deduction scheme that could "meet in the middle" by trying std::function<void(A, int)>::function<void(*)(A, int)> among (arbitrarily many) other valid instantiations of the constructor.
Problems abound with that.
It has to provably arrive at an answer. In general there are infinite possible instantiations of the templates. You'd also want it to be able to pick int(*)(A, int) if you passed it int g(A, int).
It really should agree with the current scheme where that arrives at an unambiguous answer.
Each of the compiler vendors have to implement it correctly.
As a handy workaround you can provide this kind of func_1 overload.
...
void func_1(void(*x)(A, int))
{
func_1(std::function<void(A, int)>{x});
}
Now it works as desired without static_cast: demo
I have a class that mainly wraps a std::variant with some minor additional functionality/meta-data.
For simplicity of use, I wanted to provide a user-defined conversion of this wrapper class to the underlying variant type, so functions like std::holds_alternative could be called directly on it.
What I've uncovered leaves me very confused about whether and when user-defined conversions will be applied. Here is simplified code.
#include <iostream>
#include <variant>
struct MyClass
{
// "MyClass" is implicitly convertible to a variant<bool,int>
operator std::variant <bool, int> ()
{
return std::variant<bool,int>(5);
}
};
void thisFunctionTakesOneSpecificVariantType (std::variant<bool,int> v)
{
std::cout << v.index();
}
template <class... types>
void thisFunctionTakesAnyVariantType (std::variant<types...> v)
{
std::cout << v.index();
}
int main ()
{
MyClass mc;
// 1. This compiles and runs as expected,
// proving the user-defined conversion (MyClass -> variant<int,bool>) exists and works "sometimes"
thisFunctionTakesOneSpecificVariantType (mc);
// 2. This compiles and runs as expected,
// proving "thisFunctionTakesAnyVariantType" is well defined
thisFunctionTakesAnyVariantType (std::variant <bool, int> (5));
// 3. But, combining 1 & 2, this fails to compile:
/* fails */ thisFunctionTakesAnyVariantType (mc); // error: no matching function for call to 'thisFunctionTakesAnyVariantType'
// 4. This is what i really want to do, and it also fails to compile
/* fails */ std::holds_alternative<int>(mc); // error: no matching function for call to 'holds_alternative'
// 5. An explicit conversion works for 3 and 4, but why not an implicit conversion?
// After all, the implicit conversion worked in #1
thisFunctionTakesAnyVariantType ( static_cast<std::variant <bool, int>> (mc) );
return EXIT_SUCCESS;
}
Why don't use cases 3 and 4 compile, while 1, 2, and 5 do?
In the error messages, it provides this note:
note: candidate template ignored: could not match 'variant<type-parameter-0-1...>' against 'MyClass'
inline constexpr bool holds_alternative(const variant<_Types...>& __v)
Why don't use cases 3 compile
Because implicit conversions are not considered in template argument deduction:
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
The conversion from MyClass to std::variant <bool, int> won't be considered then type deduction fails. As #5 showed you can apply explicit conversion before passing to thisFunctionTakesAnyVariantType.
Why don't use cases 4 compile
Same reason as #3. Note that even you specify some template arguments for the parameter pack template argument deduction still tries to deduce the following template arguments from the function argument. You can use exclude the function parameter from the deduction as
template <class... types>
void thisFunctionTakesAnyVariantType (std::type_identity_t<std::variant<types...>> v)
then you can call it as
thisFunctionTakesAnyVariantType<bool, int>(mc);
But note that this will make all the template argument deduction invalid (and #2 and 5 would fail), so it might be a bad idea.
BTW: std::type_identity is supported since C++20 even it's easy to implement one.
That's how the rules are laid out. Someone else with more knowledge then me might come and give you the exact rules at play here (template substitution and conversions considered), but at the end of the day it is what it is and you cannot change that.
In order for the conversion to be considered your class needs to inherit from std::variant<int, bool>. All of your examples will compile. I am however reluctant to recommend this approach as indeed composition seems the right design here.
What I would do is provide a conversion method. You lose the implicit aspect of it, but that maybe it's not such a bad idea, at least considering the alternative.
struct MyClass
{
std::variant<bool, int> to_var() const
{
return {5};
}
}
thisFunctionTakesAnyVariantType (mc.to_var());
std::holds_alternative<int>(mc.to_var());
You could leave the conversion operator for those situation where it works but consider if it doesn't just add confusion.
I am writing a library with many function objects whose classes have several operator() overloads that do not depend on the state of the classes and do not alter it. Now, I tried to make my code work with many old-style APIs (it is not a random need, I actually had to deal with such APIs) and therefore decided to make the function objects convertible to any function pointer corresponding to one of the overloads. At some point, I realized that I had too many such conversions to function pointer operators and that I should theorically be able to write a single variadic conversion operator. Here is a class implementing such a variadic operator:
struct foobar
{
template<typename... Args>
using fptr_t = void(*)(Args... args);
template<typename... Args>
operator fptr_t<Args...>() const
{
return [](Args... args) {
// Whatever
};
}
};
As you can see, I used the lambda conversion to function pointer to implement the conversion operator, which is not a problem since every function object I have is stateless. The goal was to be able to use the class as follows:
int main()
{
void(*foo)(int) = foobar();
void(*bar)(float, double) = foobar();
}
g++ has no problem compiling this code with the expected semantics. However, clang++ rejects it with a template substitution failure error:
main.cpp:21:11: error: no viable conversion from 'foobar' to 'void (*)(int)'
void(*foo)(int) = foobar();
^ ~~~~~~~~
main.cpp:11:5: note: candidate function [with Args = int]
operator fptr_t<Args...>() const
^
1 error generated.
Note that clang++ has no problem with such conversion operators as long as no variadic templates are involved. If I use a single template parameter, it will have no problem compiling the code. Now, should the code above be accepted or rejected by the compiler?
A lambda can only be converted to a function pointer if it does not capture, so your code should work. This is justified in the standard 5.1.2/p6 Lambda expressions [expr.prim.lambda] (Emphasis Mine):
The closure type for a non-generic lambda-expression with no
lambda-capture has a public non-virtual non-explicit const conversion
function to pointer to function with C++ language linkage (7.5) 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.
So I would file it as a CLANG bug.
As a work around for CLANG, you can convert it to a std::function as shown below:
struct foobar
{
template<typename... Args>
using fptr_t = void(*)(Args... args);
template<typename... Args>
operator std::function<void(Args...)>() const
{
return [](Args... args) {
//...
};
}
};
int main()
{
std::function<void(int)> f1 = foobar();
std::function<void(double, float)> f2 = foobar();
f1(1);
f2(2.0, 1.0f);
}
Live Demo
I have the following piece of code:
#include <functional>
template <typename T>
class TD; // For displaying type
void f(int, int, int) { }
int main() {
auto g = std::bind(f, std::placeholders::_1, 2, 2);
TD<decltype(g)> td1;
return 0;
}
In this code TD is a template trick for showing its template argument passed via decltype.
Output of the compiler follows (compiled in C++14 mode):
prog.cpp: In function 'int main()':
prog.cpp:10:18: error: aggregate 'TD<std::_Bind<void (*(std::_Placeholder<1>, int, int))(int, int, int)> > td1' has incomplete type and cannot be defined
TD<decltype(g)> td1;
^
Well, incomplete type error is of course excepted. But what makes me curious in this error message is std::_Bind<void (*(std::_Placeholder<1>, int, int))(int, int, int)>. I can comprehend that std::_Bind is a proxy class which defines operator() and makes our purpose possible. But its template argument void (*(std::_Placeholder<1>, int, int))(int, int, int) made me woow! How should I interpret it? Does it have any usefulness in user-land code? How can I create my own classes making use of this declaration?
void (*(std::_Placeholder<1>, int, int))(int, int, int)
This declares an unnamed function taking three parameters (std::_Placeholder<1>, int and int) and returning a pointer to a function that takes three ints and returns void.
Let's simplify a little. First consider a simple function declaration:
void f(int)
Now, in parameter declarations (of functions or templates) you can omit the name and you get
void (int)
which, if used in a parameter list of a function declaration, would be equivalent to a function pointer void(*)(int).
A function that returns a function pointer is declared like this:
void (*f(int))(int);
// ^ ^ <- this pair of parentheses changes
// the order in which the declaration is parsed.
// Without it, the return type would be `void*`
// and you'd get a syntax error
Now you can remove the name f and you basically get the same thing that you were asking about.
Its uses? Apparently it's useful when implementing std::bind :) I can't think of anything else right now...
C++11 marked the advent of function which made defining function pointers far easier:
Instances of std::function can store, copy, and invoke any Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.
So for example let's say that you needed to take in a function pointer to string foo(int param) { return to_string(param); } in one of your functions. Pre-C++11, your function would need to look like:
void bar(string (*func)(int)) { cout << func(13) << endl; }
Let's go a step further and say you wanted to expand foo to: string foo2(int lhs, int rhs) { return to_string(lhs + rhs); }. But now you want to cram that back into bar. Doing this bar(bind(&foo2, placeholders::_1, 42)); would give you an error like this:
cannot convert 'std::_Bind_helper (*)(int, int), const std::_Placeholder<1>&, int>::type {aka std::_Bind (*(std::_Placeholder<1>, int))(int, int)>}' to 'std::string (*)(int) {aka std::basic_string (*)(int)}' for argument '1' to 'void bar(std::string (*)(int))'
You could get around this error by creating a function that took an implementation specific argument, like: void bar2(_Bind<string (*(_Placeholder<1>, int))(int, int)> func) { cout << func(13) << endl; } which could successfully be called with: bar2(bind(&foo2, placeholders::_1, 42));. The reason that this is implementation specific is that the types: _Bind and _Placeholder are non-standard. In fact the return of bind is:
A function object of unspecified type T
Which brings us to function. If you weren't already turned off by the syntax the limitations of a function pointer, you'll need to take a function parameter to accept an object created by bind. Let's make a new bar using function:
void bar3(function<string(int)> func) { cout << func(13) << endl; }
This is capable of accepting both the traditional function pointer and the bind functor. Additionally it can handle lambdas, so you can do this: bar3([](int param) { return to_string(param); });
I've created a live example so you can play around with this some hopefully the benefits of the function object are clear.
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.