Member function pointer issue with standard library methods - c++

This question is spawned from
Passing a member function pointer to an overloaded class method into a template function.
You need not read that to understand this question. Probably both the questions will have the same answer.
I am getting compiler error for below simple code.
#include<set>
template<typename Return, typename T>
T ReceiveFuncPtr (Return (T::*Method)(const int&))
{
T obj; // Found and declared an object of actual container class
(obj.*Method)(1); // Some processing
return obj; // Returned that container class object with RVO
}
int main ()
{
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
}
The error is interesting:
In function 'int main()':
error: no matching function for call to 'ReceiveFuncPtr(<unresolved overloaded function type>)'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: candidate is:
note: template<class Return, class T> T ReceiveFuncPtr(Return (T::*)(const int&))
T ReceiveFuncPtr (Return (T::*Method)(const int&))
^
note: template argument deduction/substitution failed:
note: mismatched types 'const int&' and 'std::initializer_list<int>'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::value_type&& {aka int&&}'
note: couldn't deduce template parameter 'Return'
If you look at the notes closely then it appears that compiler is matching all the other methods except the right one! In this case compiler should have matched insert(const std::set<int>::value_type&) aka const int&. If I change the ReceiveFuncPtr() to match some other overload, it will again fail by skipping that overload.
To debug this situation, I created handcrafted version of std::set. But that compiles fine:
template<typename T, typename T2 = void>
struct MySet
{
std::pair<T,bool> insert (const T& i) { return std::pair<T,bool>(T(),true); }
std::pair<T,bool> insert (T&& i) { return std::pair<T,bool>(T(),true); }
void insert (std::initializer_list<T> i) { return false; }
}
int main ()
{
ReceiveFuncPtr(&MySet<int>::insert); // OK
}
After surfing, I came across this post:
What are the rules for function pointers and member function pointers to Standard functions?
Though it's related , it doesn't solve problem.
Question: Why member function substitution fails in case of standard library method when the the same thing passes for handwritten class method?
Update:
After looking at the correct answer, I am sure that insert cannot be used. The only way would be ugly typecasting which is an overkill for this problem.
One elegant solution is to use std::set<int>::emplace<const int&> which has only templated version unlike insert which has mix of template and non-template versions.
Call the function as below:
ReceiveFuncPtr(&std::set<int>::emplace<const int&>);
Above compiles fine.

The problem isn't with the insert functions you showed in MySet. The problem is with one of the ones you omitted. Specifically:
template< class InputIt >
void insert( InputIt first, InputIt last );
From [temp.deduct.call]:
When P is a function type, pointer to function type, or pointer to member function type:
— If the argument is an overload set containing one or more function templates, the parameter is treated
as a non-deduced context.
Since &std::set<int>::insert is precisely such an overload set, the parameter is a non-deduced context and cannot be resolved. Your example of MySet does not contain a function template overload for insert, which is why it works fine. If you add one, you'll see that it will also fail to compile.

Related

Error "lambda is not derived from 'std::function'

I'm trying to pass lambda to std::function<> defined via variadic template, but it seems that this doesn't work on gcc.
Is there any reason, why this code doesn't work on gcc 7.4.0 but working correctly on Visual Studio 2017? And is there any way how to make it work also on gcc without the necessity to manually convert it to std::function<> first?
#include <functional>
template<class ...TParams>
int TestFunction(std::function<void(TParams...)> )
{
return 0;
}
void Test()
{
auto fce = [](int /*n*/, double /*d*/) {};
//This doesn't work with error no matching function for call to 'TestFunction<int, double>(Test()::<lambda(int, double)>&)'
TestFunction<int, double>(fce);
//but this works correctly
std::function<void(int, double)> fce2 = fce;
TestFunction<int, double>(fce2);
}
I'm getting following error:
main.cpp: In function 'void Test()':
main.cpp:116:31: error: no matching function for call to 'TestFunction<int, double>(Test()::<lambda(int, double)>&)'
TestFunction<int, double>(fce);
^
main.cpp:106:5: note: candidate: template<class ... TParams> int TestFunction(std::function<void(TParams ...)>)
int TestFunction(std::function<void(TParams...)> fceCallback)
^~~~~~~~~~~~
main.cpp:106:5: note: template argument deduction/substitution failed:
main.cpp:116:31: note: 'Test()::<lambda(int, double)>' is not derived from 'std::function<void(TParams ...)>'
TestFunction<int, double>(fce);
^
A trailing template parameter pack always leaves room for further deduction. Specifying the first two arguments doesn't prevent you from doing something like this:
std::function<void(int, double, char)> fce3 ;
TestFunction<int, double>(fce3);
In this case, the pack will contain int, double, char, because the char was deduced from the function argument. Now, because deduction isn't over, and a lambda is not a std::function, the substitution fails.
To make this work, you need to let the deduction process know it's over, that an instantiated function is needed now, before it's given an argument. One way to do that is to take the function's address, for instance:
auto pfunc = TestFunction<int, double>;
pfunc(fce);
or
(&TestFunction<int, double>)(fce);
Taking a function template's address is another context where template argument deduction can occur. In this case, the trailing pack is deduced as empty, and you get a pointer to a function you may call.

How to implicitly convert a pointer, to a const pointer, during variadic template deduction

Imagine the case, where one (for one reason, or another) wanted to implement a function, that acts like a proxy when calling functions. One could implement it as a template, which takes a pointer to a function, and all of its arguments. However, if there is a function, that accepts a const pointer to something (e.g. int const*), and you are trying to pass just a simple pointer to function (e.g. int*), the template deduction fails, due to ambiguous template argument. Example:
#include <utility>
template <class... TArgs>
void foo (void (*pFunc)(TArgs...), TArgs... args)
{
pFunc (std::forward<TArgs> (args)...);
}
void bar (int const* pInt) {}
int main ()
{
int a = 5;
foo (bar, &a);
}
Produces:
error C2672: 'foo': no matching overloaded function found
error C2782: 'void foo(void (__cdecl *)(TArgs...),TArgs...)': template parameter 'TArgs' is ambiguous
note: see declaration of 'foo'
note: could be 'const int*'
note: or 'int*'
Even if, simply calling bar (&a) would succeed due to implicit conversion between int*, and int const*.
Yes, I realize, that one could specify the argument types directly (e.g. (foo<int const*> (bar, &a)), but given the fact, that arbitrary amount of arguments are accepted, such list would be quite long, and, would, in my personal opinion, look ugly.
Another option, would be, to liter the code with const_casts, wherever such conversion would be needed, but that would, also, inflate the code in undesirable ways (or in other words, would look ugly, as well).
The 3rd solution, that I could think of, would be to provide 2 parameter packs, like so:
template <class... TArgs, class... TArgs2>
void foo (void (*pFunc)(TArgs...), TArgs2... args)
{
pFunc (std::forward<TArgs2> (args)...);
}
Which, would solve the immediate problem of implicit argument conversion, but would introduce another problem, of compiler errors pointing to the foo implementation, instead of foo invocation, in the case of mismatched arguments to the function, which would make it harder to identify where exactly, in the code base, the mismatched call was being made. Example of errors, that I get, if I use the above example, and the following main function:
int main ()
{
float b = 5;
foo (bar, &b);
}
error C2664: 'void (const int *)': cannot convert argument 1 from 'float *' to 'const int *'
note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
note: see reference to function template instantiation 'void foo<const int*,float*>(void (__cdecl *)(const int *),float *)' being compiled
So, the question is, is there some sort of template magic, that would allow it to work in the manner, that I expect, given the restrictions, and considerations, I have given above? Or am I just being unreasonable with my requirements?
You could constrain the function:
template <class... TArgs, class... TArgs2>
enable_if_t<is_invocable_v<void(TArgs...),TArgs2...>>
foo (void (*pFunc)(TArgs...), TArgs2... args)
{
pFunc (forward<TArgs2> (args)...);
}

Could not convert from brace-enclosed initializer list to std tuple

As part of a bigger project, I'm playing with std::tuple and templates; consider the following code:
template <typename ...T> void foo(tuple<T...> t) {}
void bar(tuple<int, char> t) {}
tuple<int, char> quxx() { return {1, 'S'}; }
int main(int argc, char const *argv[])
{
foo({1, 'S'}); // error
foo(make_tuple(1, 'S')); // ok
bar({1, 'S'}); // ok
quxx(); // ok
return 0;
}
According to this answer C++17 supports tuple initialization from copy-list-initialization, however it seems such support is limited since I get the following error (GCC 7.2.0):
main.cpp: In function 'int main(int, const char**)':
main.cpp:14:17: error: could not convert '{1, 'S'}' from '<brace-enclosed initializer list>' to 'std::tuple<>'
foo({1, 'S'}); // error
^
Is there any way I can use brace-enclosed syntax in this scenario?
Some Context : this is going to be used in an operator overload so I guess I'm bound to tuples and cannot make use of variadics, any hint is well-accepted.
Extra : Clang 6 also complains
prog.cc:12:5: error: no matching function for call to 'foo'
foo({1, 'S'}); // error
^~~
prog.cc:6:31: note: candidate function [with T = <>] not viable: cannot convert initializer list argument to 'tuple<>'
template <typename ...T> void foo(tuple<T...> t) {}
A braced-init-list, like {1, 'S'}, does not actually have a type. In the context of template deduction, you can only use them in certain cases - when deducing against initializer_list<T> (where T is a function template parameter) or when the corresponding parameter is already deduced by something else. In this case, neither of those two things is true - so the compiler cannot figure out what ...T is supposed to be.
So you can provide the types directly:
foo<int, char>({1, 'S'});
Or you can construct the tuple yourself and pass that in:
foo(std::tuple<int, char>(1, 'S')); // most explicit
foo(std::tuple(1, 'S')); // via class template argument deduction
Today, ClassTemplate<Ts...> can only be deduced from expressions of type ClassTemplate<Us...> or types that inherit from something like that. A hypothetical proposal could extend that to additionally try to perform class template argument deduction on the expression to see if that deduction succeeds. In this case, {1, 'S'} isn't a tuple<Ts...> but tuple __var{1, 'S'} does successfully deduce tuple<int, char> so that would work. Such a proposal would also have to address issues like... what if we're deducing ClassTemplate<T, Ts...> or any minor variation, which isn't something that class template argument deduction allows (but is something that many people have at times expressed interest in being able to do).
I'm not aware of such a proposal today.
According to this answer C++17 supports tuple initialization from copy-list-initialization, however it seems such support is limited since I get the following error
The problem is another.
When you call bar({1, 'S'}), the compiler knows that bar() receive a tuple<int, char>, so take 1 as int and 'S' as char.
See another example: if you define
void baz (std::tuple<int> const &)
{ }
you can call
baz(1);
because the compiler knows that baz() receive a std::tuple<int> so take 1 to initialize the int in the tuple.
But with
template <typename ...T>
void foo(tuple<T...> t)
{ }
the compiler doesn't know the T... types; when you call
foo({1, 'S'});
what T... types should deduce the compiler?
I see, at least, two hypothesis: T = int, char or T = std::pair<int, char>; or also T = std::tuple<int, char>.
Which hypothesis should follows the compiler?
I mean: if you pass a std::tuple to foo(), the compiler accept the list of types in the tuple as the list of T...; but if you pass something else, the compiler must deduce the correct std::tuple; but this deduction, in this case, is not unique. So the error.

Passing a member function pointer to an overloaded class method into a template function [duplicate]

This question is spawned from
Passing a member function pointer to an overloaded class method into a template function.
You need not read that to understand this question. Probably both the questions will have the same answer.
I am getting compiler error for below simple code.
#include<set>
template<typename Return, typename T>
T ReceiveFuncPtr (Return (T::*Method)(const int&))
{
T obj; // Found and declared an object of actual container class
(obj.*Method)(1); // Some processing
return obj; // Returned that container class object with RVO
}
int main ()
{
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
}
The error is interesting:
In function 'int main()':
error: no matching function for call to 'ReceiveFuncPtr(<unresolved overloaded function type>)'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: candidate is:
note: template<class Return, class T> T ReceiveFuncPtr(Return (T::*)(const int&))
T ReceiveFuncPtr (Return (T::*Method)(const int&))
^
note: template argument deduction/substitution failed:
note: mismatched types 'const int&' and 'std::initializer_list<int>'
ReceiveFuncPtr(&std::set<int>::insert); // ERROR
^
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::const_iterator {aka std::_Rb_tree_const_iterator<int>}'
note: mismatched types 'const int&' and 'std::set<int>::value_type&& {aka int&&}'
note: couldn't deduce template parameter 'Return'
If you look at the notes closely then it appears that compiler is matching all the other methods except the right one! In this case compiler should have matched insert(const std::set<int>::value_type&) aka const int&. If I change the ReceiveFuncPtr() to match some other overload, it will again fail by skipping that overload.
To debug this situation, I created handcrafted version of std::set. But that compiles fine:
template<typename T, typename T2 = void>
struct MySet
{
std::pair<T,bool> insert (const T& i) { return std::pair<T,bool>(T(),true); }
std::pair<T,bool> insert (T&& i) { return std::pair<T,bool>(T(),true); }
void insert (std::initializer_list<T> i) { return false; }
}
int main ()
{
ReceiveFuncPtr(&MySet<int>::insert); // OK
}
After surfing, I came across this post:
What are the rules for function pointers and member function pointers to Standard functions?
Though it's related , it doesn't solve problem.
Question: Why member function substitution fails in case of standard library method when the the same thing passes for handwritten class method?
Update:
After looking at the correct answer, I am sure that insert cannot be used. The only way would be ugly typecasting which is an overkill for this problem.
One elegant solution is to use std::set<int>::emplace<const int&> which has only templated version unlike insert which has mix of template and non-template versions.
Call the function as below:
ReceiveFuncPtr(&std::set<int>::emplace<const int&>);
Above compiles fine.
The problem isn't with the insert functions you showed in MySet. The problem is with one of the ones you omitted. Specifically:
template< class InputIt >
void insert( InputIt first, InputIt last );
From [temp.deduct.call]:
When P is a function type, pointer to function type, or pointer to member function type:
— If the argument is an overload set containing one or more function templates, the parameter is treated
as a non-deduced context.
Since &std::set<int>::insert is precisely such an overload set, the parameter is a non-deduced context and cannot be resolved. Your example of MySet does not contain a function template overload for insert, which is why it works fine. If you add one, you'll see that it will also fail to compile.

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