C++:使用同名但参数不同的 class 成员和静态函数失败

C++: using class member and static function of same name but different parameters fails

我想要一个 class,它有一个静态函数和一个同名的成员函数,并且做完全相同的事情。一次能够从实例中调用它,一次将它与标准算法中的函数一起使用。最小示例:

#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 都发生了可怕的错误。 clang 错误是

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报错基本一样,只是写法不同

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

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

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

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

  • 创建静态成员,友元函数
  • 使用 lambda

我推荐第二种,那就这样吧:

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

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

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

static成员不适合count_if这一事实在推断模板参数的类型时没有考虑。

如果您的 class 过载

,您会发现同样的错误
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 成员函数移出 class。使其成为全局函数或您自己的应用程序的命名空间中的函数。

#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.