6

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.

Community
  • 1
  • 1
iammilind
  • 68,093
  • 33
  • 169
  • 336
  • it actually compiles and work well on MSVC with visual studio 2013 : `template T exec(Ret(T::*func)(const int& i)){ T t; (t.*func)(4); return t; }` – David Haim Jul 09 '15 at 11:36

1 Answers1

7

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Yes, with adding a `template` function it [doesn't compile](http://ideone.com/SF5EbB). I skipped that function, thinking it to be irrelevant for this example :-). It would be a bonus, if you can come up with an elegant solution for the actual problem, i.e. how to implement `ReceiveFuncPtr()` with this constraints. – iammilind Jul 09 '15 at 11:54
  • @iammilind You'd have to cast it to the right type. In this case, that's a mouthful: `ReceiveFuncPtr(static_cast::iterator, bool> (std::set::*)(const int&)>(&std::set::insert));` Or you could write a class with no such function template `insert`. – Barry Jul 09 '15 at 11:58
  • I have updated your answer with a possible solution, which I am going to use now. To use `emplace()` function. Hope you are ok with it. :-) – iammilind Jul 09 '15 at 12:19
  • 1
    @iammilind `emplace` works because you explicitly provide the right function, not because it's only templated. – Barry Jul 09 '15 at 12:20
  • @Barry, that's correct. My perception is: In case of `emplace`, we don't have to do ugly typecasting like how we have to do for `insert`. Just 1 extra `template` argument has to be added. In short, I am thinking from "clean looking" perspective. – iammilind Jul 09 '15 at 12:45