2016-11-16 2 views
2

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. Betrachten Sie den folgenden Code ein:In C++ 11 gibt es eine Möglichkeit, keine Template-Argumente zu benötigen, wenn man eine Funktion aufruft, die ein beliebiges Callable als Argument akzeptiert, einschließlich gebundener Methoden?

#include <list> 

int timesTwo(int x) { return x*2; } 
int timesX(int x, int y) { return x*y; } 

class Foo { 
public: 
    Foo(int a) : value(a) {}; 
    int fooTimesTwo() const { return value*2; }; 
    int value; 
}; 

template <class T, class U> 
std::list<U> myMap(const std::list<T> &list, const std::function<U(const T &)> &func) 
{ 
    std::list<U> result; 

    for(typename std::list<T>::const_iterator it = list.begin(); it != list.end(); ++it) { 
     result.push_back(func(*it)); 
    } 

    return result; 
} 

int main() 
{ 
    std::list<int> numbers = {1,2,3,4,5}; 
    std::list<int> numbers2 = myMap<int,int>(numbers, [] (int x) { return x*2; }); 
    std::list<int> numbers3 = myMap<int,int>(numbers, &timesTwo); 
    std::list<int> numbers4 = myMap<int,int>(numbers, std::bind(timesX, 2, std::placeholders::_1)); 

    std::list<Foo> fooList = {Foo(1), Foo(2), Foo(3), Foo(4)}; 
    std::list<int> numbers5 = myMap<Foo,int>(fooList, &Foo::fooTimesTwo);  
    return 0; 
} 

Gibt es trotzdem zu umschreiben myMap so dass

  1. alle vier der Anrufe, um es in meinem Code Beispiel keine Vorlage Argumente benötigen, und ...

  2. es gibt nur eine allgemeine Implementierung, und ich muss nicht manuell eine überladene Version für jede Kombination von Typen schreiben, die ich es nennen möchte?

Ich habe versucht, das zweite Argument von myMap Ändern ein dritte Templat-Typ zu sein, statt std::function, aber es funktioniert nicht, weil a) der zweite Templat Typ U nicht geschlossen werden kann, und b) wenn es sogar könnte der vierte Aufruf an myMap einen Fehler in Zeile 20 verursachen, da &Foo::fooTimesTwo kein Funktions- oder Funktionszeiger ist.

Ich bin bereit, alle der verschiedenen Features von C++ 11 zu berücksichtigen, um dies zu tun, und es ist mir nicht besonders wichtig, ob es die Deklaration oder Definition von myMap stottern oder unlesbar macht. Ich frage mich nur, ob es möglich ist und wenn ja, welche Art von Techniken und C++ 11 Features können es erreichen.

Antwort

3

Sie könnten versuchen, std::invoke in C++ 11 zu implementieren, aber ich denke, es wäre wirklich umständlich.

Es ist ziemlich einfach, eine Funktion Vorlage für eine generali aufrufbar zu machen:

template <class T, class F> 
auto myMap(const std::list<T> &list, F&& func) 
    -> std::list<typename std::decay<decltype(func(*list.begin()))>::type> 
{ 
    using U = typename std::decay<decltype(func(*list.begin()))>::type; 
    std::list<U> result; 

    for(typename std::list<T>::const_iterator it = list.begin(); it != list.end(); ++it) { 
     result.push_back(func(*it)); 
    } 

    return result; 
} 

Sie erhalten Ausdruck sfinae kostenlos. Jetzt gibt es nur Mitgliedsfunktionszeiger, um der gesorgt werden muss:

template <class T, class F> 
auto myMap(const std::list<T> &list, F&& func) 
    -> std::list<typename std::decay<decltype(((*list.begin()).*func)())>::type> 
{ 
    return myMap(list, [=](T const& t){ return (t.*func)(); }); 
} 

Jetzt können Sie Ihre Funktionen wie erwartet aufrufen. demo


Während es an, Sie dieses hässliche for Schleife mit reichten für ersetzen könnte:

for(auto const& elem : list) { 
    results.push_back(func(elem)); 
} 

Oder verwenden Sie einen Algorithmus:

std::transform(list.begin(), list.end(), std::back_inserter(result), func); 
+1

Dank dafür! Wenn ich Ihre Antwort studiert habe, habe ich ziemlich viel über Auto, Decltype und Decay gelernt. – GuyGizmo

Verwandte Themen