【问题标题】:Member function pointer issue with standard library methods标准库方法的成员函数指针问题
【发布时间】:2015-09-27 18:06:16
【问题描述】:

这个问题来自
Passing a member function pointer to an overloaded class method into a template function
您无需阅读即可理解这个问题。可能这两个问题都会有相同的答案。

我收到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
}

这个错误很有趣:

 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'

如果您仔细查看注释,那么编译器似乎正在匹配除正确方法之外的所有其他方法!在这种情况下,编译器应该匹配 insert(const std::set&lt;int&gt;::value_type&amp;) aka const int&amp;。如果我更改 ReceiveFuncPtr() 以匹配其他重载,它将再次通过跳过该重载而失败。

为了调试这种情况,我创建了 std::set 的手工版本。但是那个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
}

上网后偶然发现这个帖子:
What are the rules for function pointers and member function pointers to Standard functions?

虽然有关系,但解决不了问题。

问题:为什么在标准库方法的情况下,当手写类方法通过相同的事情时,成员函数替换失败?

更新

看了正确答案后,我确定insert是不能用的。唯一的方法是丑陋的类型转换,这对于这个问题来说太过分了。
一种优雅的解决方案是使用std::set&lt;int&gt;::emplace&lt;const int&amp;&gt;,它只有templated 版本,而insert 则混合了template 和非template 版本。
调用函数如下:

ReceiveFuncPtr(&std::set<int>::emplace<const int&>);

以上compiles fine

【问题讨论】:

  • 它实际上可以在带有 Visual Studio 2013 的 MSVC 上编译并运行良好:template &lt;class T, class Ret&gt; T exec(Ret(T::*func)(const int&amp; i)){ T t; (t.*func)(4); return t; }

标签: c++ templates c++11 standard-library member-function-pointers


【解决方案1】:

问题不在于您在MySet 中显示的insert 函数。问题在于您省略的其中之一。具体来说:

template< class InputIt >
void insert( InputIt first, InputIt last );

来自[temp.deduct.call]:

当 P 是函数类型、指向函数类型的指针或指向成员函数类型的指针时:
— 如果参数是包含一个或多个函数模板的重载集,则处理参数 作为非推断上下文。

由于&amp;std::set&lt;int&gt;::insert正是这样一个重载集,所以参数是非推导上下文,无法解析。您的MySet 示例不包含insert 的函数模板重载,这就是它工作正常的原因。如果你加一个,你会看到它也会编译失败。

【讨论】:

  • 是的,通过添加template 函数doesn't compile。我跳过了那个函数,认为它与这个例子无关:-)。如果你能为实际问题想出一个优雅的解决方案,那将是一个额外的好处,即如何在这个约束下实现ReceiveFuncPtr()
  • @iammilind 您必须将其转换为正确的类型。在这种情况下,这是一口:ReceiveFuncPtr(static_cast&lt;std::pair&lt;std::set&lt;int&gt;::iterator, bool&gt; (std::set&lt;int&gt;::*)(const int&amp;)&gt;(&amp;std::set&lt;int&gt;::insert)); 或者你可以编写一个没有这样的函数模板的类insert
  • 我已经用一个可能的解决方案更新了您的答案,我现在将使用它。使用emplace() 功能。希望你没问题。 :-)
  • @iammilind emplace 之所以有效,是因为您明确提供了正确的功能,而不是因为它只是模板化的。
  • @Barry,没错。我的看法是:在emplace 的情况下,我们不必像insert 那样进行丑陋的类型转换。只需添加 1 个额外的 template 参数。简而言之,我是从“干净”的角度思考的。
猜你喜欢
  • 2011-09-09
  • 1970-01-01
  • 2010-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多