2016-11-22 3 views
0

Um mein Verständnis von C++ 11 zu erweitern, experimentiere ich damit, funktionale Helfer zu schreiben und zu sehen, ob ich sie weniger ausführlich nennen kann. Im Moment versuche ich eine some Funktion zu schreiben, die wahr zurückgibt, wenn irgendein Gegenstand in einer Sammlung einen Test besteht. Ich möchte, dass es mit jedem Sammlungs-Typ funktioniert, in der Lage ist, aufrufbar zu sein und keine Template-Argumente zu benötigen.C++: Wie kann ich eine Funktion überladen, so dass es jeden Funktor einschließlich Zeiger auf Elementfunktionen nehmen kann?

Im Grunde möchte ich es so, dass der folgende Code arbeiten kompilieren:

// 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; 
} 

Beachten Sie, dass, wenn der Wert Art der Sammlung ein Objekt oder Objektzeiger, ich möchte weitergeben können, Zeiger auf eine Member-Funktion als zweites Argument zu some. Und hier werden die Dinge haarig.

Mein erster Versuch war es wie folgt umzusetzen:

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)(); }); 
} 

... aber es funktioniert nicht, wenn die Sammlung nicht STL Sammlung ist, oder zumindest eine, die beiden Template-Argumente übernimmt. In meinem Beispiel funktioniert die Verwendung von DerivedFooList oder DerivedFooPtrList nicht.

Mein zweiter Versuch sah wie folgt aus:

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)(); }); 
} 

, die jetzt mit DerivedFooList funktioniert, aber funktioniert nicht mit std::list<Foo *> oder DerivedFooPtrList.

Mein dritte Versuch war:

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)(); }); 
} 

für die Zeit ignorieren ist, dass dies nur mit Sammlungen funktionieren würde, die ein Mitglied haben value_type genannt, es immer noch nicht meinem obiges Beispiel nicht erlaubt zu kompilieren. Ich denke (aber bin nicht 100% sicher) der Grund ist, dass es T für die zweiten zwei Versionen von some für die Fälle nicht ableiten kann, in denen ich es verwenden möchte.

Ich habe sehr hart versucht zu sehen, ob es eine Möglichkeit gibt, was ich will aus C++ 11, und ich vermute, dass es ist, aber ich kann es nicht herausfinden. Ist was ich will, und wenn ja, wie mache ich das?

+1

Nur bis zu C++ 11? Wie wäre es mit C++ 17? –

+0

Ich fürchte, ich kann C++ 17 nicht für dieses Projekt verwenden. – GuyGizmo

Antwort

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 Überlastungen () die INVOKE Konzept zu tun, die in C++ 17 direkt über std::invoke zugegriffen werden kann. Ihre Anforderungen scheinen mit dem INVOKE Konzept übereinzustimmen.

decltype(x)(x) entspricht einem std::forward Ausdruck, wenn x eine abgeleitete auto&& Variable ist. Lies es als "behandle x so, als ob es der Typ wäre, für den es erklärt wurde". Beachten Sie, dass, wenn x ein Wert ist, es im Gegensatz zu forward kopiert.

INVOKE(pmf, ptr) und INVOKE(pmf, ref) beide arbeiten. Also keine Notwendigkeit, in der Überladung ausgefallene Sachen zu machen.

+0

Das macht den Trick! Das Beste von allem ist, dass ich keine der Funktionen überlasten muss, die ich schreibe. Kannst du mir helfen zu verstehen, warum man 'declltype (x) (x)' braucht?Was kann schief gehen, wenn ich es ausschließe? – GuyGizmo

+0

@guy, wenn 'r' ein Eingabe-Iterator-Bereich von' T' ist, 'x' wäre ein L-Wert, während' ddeclt6pe (x) (x) 'ein R-Wert ist. Und Bindung an einen Wert ist Unsinn, ineffizient und allgemein falsch. Für die meisten herkömmlichen Container kein Unterschied. Aber kein Schaden für. – Yakk

+0

Verwenden Sie einfach 'const' für Klarheit und Effizienz. Vermeide zu subtile Dinge und dunkle Ecken der Sprache. –

Verwandte Themen