Member function pointer issue with standard librar

2020-03-20 13:20发布

问题:

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.

回答1:

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.