C++:我怎样才能重载一个函数,以便它可以接受任何仿函数,包括指向成员函数的指针?
C++: how can I overload a function so that it can take any functor, including pointer to member functions?
为了拓宽我对 C++11 的理解,我正在尝试编写函数式助手,看看我是否可以让它们的调用不那么冗长。现在我正在尝试编写一个 some
函数,如果集合中的任何项目通过测试,该函数 returns 为真。我希望它适用于任何集合类型,能够接受任何可调用对象,并且不需要模板参数。
基本上,我希望它能够编译以下代码:
// insert declaration of some here
#include <list>
#include <functional>
class Foo {
public:
bool check() const { return true; };
};
class DerivedFooList : public std::list<Foo> {
public:
DerivedFooList(std::initializer_list<Foo> args) : std::list<Foo>(args) {};
};
class DerivedFooPtrList : public std::list<Foo *> {
public:
DerivedFooPtrList(std::initializer_list<Foo *> args) : std::list<Foo *>(args) {};
};
bool intCheck(int a) { return a == 1; }
bool fooCheck(const Foo &a) { return a.check(); }
bool fooPtrCheck(const Foo *a) { return a->check(); }
int main()
{
Foo a, b, c;
std::list<int> intList = {1, 2, 3};
std::list<Foo> fooList = {a, b, c};
std::list<Foo *> fooPtrList = {&a, &b, &c};
DerivedFooList derivedFooList = {a, b, c};
DerivedFooPtrList derivedFooPtrList = {&a, &b, &c};
auto localIntCheck = [] (int a) { return a == 1; };
auto localFooCheck = [] (const Foo &a) { return a.check(); };
auto localFooPtrCheck = [] (const Foo *a) { return a->check(); };
some(intList, [] (int a) { return a == 1; });
some(intList, &intCheck);
some(intList, localIntCheck);
some(fooList, [] (const Foo &a) { return a.check(); });
some(fooList, &fooCheck);
some(fooList, localFooCheck);
some(fooList, &Foo::check);
some(fooPtrList, [] (const Foo *a) { return a->check(); });
some(fooPtrList, &fooPtrCheck);
some(fooPtrList, localFooPtrCheck);
some(fooPtrList, &Foo::check);
some(derivedFooList, [] (const Foo &a) { return a.check(); });
some(derivedFooList, &fooCheck);
some(derivedFooList, localFooCheck);
some(derivedFooList, &Foo::check);
some(derivedFooPtrList, [] (const Foo *a) { return a->check(); });
some(derivedFooPtrList, &fooPtrCheck);
some(derivedFooPtrList, localFooPtrCheck);
some(derivedFooPtrList, &Foo::check);
return 0;
}
注意,如果集合的值类型是对象或对象指针,我希望能够将指向成员函数的指针作为第二个参数传递给some
。这就是事情变得多毛的地方。
我的第一次尝试是这样实现的:
template <class T, class F>
bool some(const T &list, F &&func)
{
for(auto item : list) {
if (func(item)) {
return true;
}
}
return false;
}
template <template<class, class> class T, class U, class V, class W>
bool some(const T<U, V> &list, bool (W::*func)() const)
{
return some(list, [=](U const &t){ return (t.*func)(); });
}
template <template<class, class> class T, class U, class V, class W>
bool some(const T<U *, V> &list, bool (W::*func)() const)
{
return some(list, [=](U const *t){ return (t->*func)(); });
}
...但如果该集合不是 STL 集合,或者至少是采用两个模板参数的集合,则它不起作用。在我的示例中,使用 DerivedFooList
或 DerivedFooPtrList
将不起作用。
我的第二次尝试是这样的:
template <class T, class F>
bool some(const T &list, F &&func)
{
for(auto item : list) {
if (func(item)) {
return true;
}
}
return false;
}
template <class T, class U>
bool some(const T &list, bool (U::*func)() const)
{
return some(list, [=](U const &t) { return (t.*func)(); });
}
现在适用于 DerivedFooList
,但不适用于 std::list<Foo *>
或 DerivedFooPtrList
。
我的第三次尝试是:
template <class T, class F>
bool some(const T &list, F &&func)
{
for(auto item : list) {
if (func(item)) {
return true;
}
}
return false;
}
template <class T, class U>
bool some(typename std::enable_if<std::is_class<typename T::value_type>::value, T>::type const &list, bool (U::*func)() const)
{
return some(list, [=](U const &t) { return (t.*func)(); });
}
template <class T, class U>
bool some(typename std::enable_if<std::is_pointer<typename T::value_type>::value, T>::type const &list, bool (U::*func)() const)
{
return some(list, [=](U const *t) { return (t->*func)(); });
}
暂时忽略这只适用于具有名为 value_type
的成员的集合,它仍然不允许我上面的示例编译。我认为(但不是 100% 确定)原因是它无法为我希望使用它的情况推断出 some
的后两个版本的 T。
我非常努力地想看看是否有办法从 C++11 中得到我想要的东西,我怀疑有,但我想不通。我想要的是可能的吗?如果可以,我该怎么做?
template<class R, class F>
bool some( R const& r, F&& f ) {
for(auto&& x:r)
if (std::ref(f)(decltype(x)(x)))
return true;
return false;
}
std::ref
重载 ()
来执行 INVOKE
概念,在 C++17 中可以通过 std::invoke
直接访问。您的要求似乎符合 INVOKE
概念。
x
是推导的 auto&&
变量,decltype(x)(x)
等价于 std::forward
表达式。读作 "treat x
as if it was the type it was declared as"。请注意,如果 x
是一个 auto
值,它会复制,这与 forward
.
INVOKE( pmf, ptr )
和 INVOKE( pmf, ref )
都有效。所以不需要在重载中做花哨的事情。