【问题标题】:C++: using class member and static function of same name but different parameters failsC++:使用同名但不同参数的类成员和静态函数失败
【发布时间】:2018-03-10 21:08:56
【问题描述】:

我希望有一个具有静态和同名成员函数的类,并且做完全相同的事情。一次可以从实例中调用它,一次可以将它与标准算法中的函数一起使用。最小的例子:

#include <algorithm>
#include <vector>

class foo {
public:
  inline static bool isOne(const foo & s) {
    return s.bar == 1;
  }
  // if I uncomment the next line, count_if won't compile anymore
  //inline bool isOne() const { return isOne(*this); }

private:
  int bar;
};

int main()
{
  std::vector<foo> v;
  auto numones=std::count_if(v.begin(), v.end(), foo::isOne);
  return 0;
}

上面的代码按预期编译和工作。但是,如果我取消注释成员函数 isOne(),因为,也许,我也想拥有

foo x; x.isOne();

在我的 main() 中,clang 6.0 和 gcc 5.3 的情况都非常糟糕。铿锵的错误是

no matching function for call to 'count_if'
note: candidate template ignored: couldn't infer template argument '_Predicate'
count_if(_InputIterator __first, _InputIterator __last, _Predicate __pred)

和gcc的错误基本上是一样的,换个说法。

我显然做错了,但我目前不知道如何解决这个问题。任何指针表示赞赏。

【问题讨论】:

    标签: function c++11 static stl member


    【解决方案1】:

    当获取指向重载方法的指针时,您需要告诉编译器您想要获取指针指向的重载,您可以通过将静态强制转换为适当的方法类型来做到这一点:

     auto numones=std::count_if(v.begin(), v.end(), static_cast<bool(*)(const foo&)>(foo::isOne));
    

    【讨论】:

    • 谢谢。这确实有效。但也添加了很多样板代码,我觉得这对课堂用户来说有点不必要和适得其反。我可以更改课程设计/定义中的任何内容以使事情变得更容易吗?
    • 使你的函数名称不同。它是 c++ 的一个不幸的角落,没有简单的方法来获取指向方法/函数的特定重载的指针
    • 你也可以为常用的方法类型发布 typedef,这至少会使 static_cast 更短
    【解决方案2】:

    问题是因为count_if是一个函数模板,而不是一个函数。

    在推导模板参数的类型时,不考虑非static成员不适合count_if的事实。

    如果你的类有重载,你会注意到同样的错误

    inline static bool isOne(const foo & s, int) { ... }
    

    解决此问题的唯一方法是帮助编译器解决重载问题。那是通过显式转换函数来实现的

    auto numones = std::count_if(v.begin(), v.end(),
                                 static_cast<bool(&)(const foo&)>(foo::isOne));
    

    或使用显式模板参数。

    auto numones = std::count_if<decltype(v.begin()),  // First template parameter
                                 bool(&)(const foo&)>  // Second template parameter
                                 (v.begin(), v.end(), foo::isOne);
    

    您可以通过遵循良好的软件工程实践来避免这些问题。

    static 成员函数移出类。使其成为全局函数或您自己应用程序的命名空间中的函数。

    #include <algorithm>
    #include <vector>
    
    namespace MyApp
    {
       class foo {
          public:
             inline bool isOne() const { return (bar == 1); }
    
          private:
             int bar;
       };
    
       inline bool isOne(foo const& s)
       {
          return s.isOne();
       }
    }
    
    int main()
    {
      std::vector<MyApp::foo> v;
      auto numones=(v.begin(), v.end(), MyApp::isOne);
      return 0;
    }
    

    鉴于此,您可以使用 ADL 调用函数的命名空间版本,而无需显式使用 MyApp::isOne

    MyApp::foo f;
    isOne(f);   // OK. Uses ADL to find the right function.
    

    【讨论】:

    • 虽然我不喜欢让专门的功能或多或少地污染命名空间(无论是全局的还是其他的),但从用户的角度来看,您的解决方案似乎会导致最干净/最简单的代码那个班的。谢谢,已接受。
    【解决方案3】:

    还有其他几种方法可以解决这个问题,即:

    • 将静态成员设为好友函数
    • 使用 lambda

    我推荐第二个,所以这样做:

    auto numones=std::count_if(v.begin(), v.end(), [](foo const& f) { return foo::is one(f); });
    

    此解决方案允许您保留这两个功能并且不会引入任何歧义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多