C ++:如何重载函数以便它可以使用任何函子,包括指向成员函数的指针?

时间:2016-11-22 01:28:49

标签: c++ c++11 templates lambda argument-deduction

为了扩展我对C ++ 11的理解,我正在尝试编写功能助手,看看我是否可以将它们称为更简洁。现在我正在尝试编写一个some函数,如果集合中的任何项目通过测试,则返回true。我希望它适用于任何集合类型,能够获取任何可调用的,而不需要模板参数。

基本上,我希望它能够工作,以便编译以下代码:

// 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集合,或者至少有一个带有两个模板参数的集合,则它不起作用。在我的示例中,使用DerivedFooListDerivedFooPtrList将不起作用。

我的第二次尝试看起来像这样:

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中得到我想要的东西,我怀疑它有,但我无法弄明白。是我想要的,如果是的话,我该怎么做?

1 个答案:

答案 0 :(得分:2)

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概念,可以通过std::invoke直接访问C ++ 17中的概念。您的要求似乎符合INVOKE概念。

如果decltype(x)(x)是推断的std::forward变量,则

x相当于auto&&表达式。阅读它作为&#34;将x视为它被声明为&#34;的类型。请注意,如果xauto值,则会复制,与forward不同。

INVOKE( pmf, ptr )INVOKE( pmf, ref )都有效。因此,无需在过载中做任何花哨的东西。