2016-07-11 5 views
0

Ich brauchte einen Weg, wie die Ausführungszeit einer Funktion zu messen. Ich fand diese sehr gute Antwort auf SO https://stackoverflow.com/a/21995693/3179492. Es ist eine perfekte Lösung.Warum kann der Compiler nicht um die ungültige Verwendung der nicht statischen Elementfunktion herum arbeiten

Es verwendet einen Funktionszeiger mit variadischen Parameterliste. Hier

ist die MCVE:

#include <algorithm> 
#include <chrono> 
#include <stdio.h> 

class Foo 
{ 
public: 
    auto foo_member(int x) -> void { printf("in foo_member(%d);\n", x); } 
}; 

class measure 
{ 
public: 
    template<typename F, typename ...Args> 
    static typename std::chrono::microseconds::rep execution(F func, Args&&... args) 
    { 
     auto start = std::chrono::system_clock::now(); 
     func(std::forward<Args>(args)...); 
     auto duration = std::chrono::duration_cast<std::chrono::microseconds> 
            (std::chrono::system_clock::now() - start); 
     return duration.count(); 
    } 
}; 

int main() 
{ 
     Foo foo; 
     int x = 1234; 

     // this line does not compile  
     printf("execution time=%ld\n", measure::execution(foo.foo_member, x)); 
} 

Dieser Code kompiliert nicht, weil foo_member nicht statisch ist. Die Fehlermeldung lautet invalid use of non-static member function.

Es gibt einige Möglichkeiten, wie Sie das Problem lösen können. Für mich ist die eleganteste, kürzeste und effektivste Weg ist dies:

printf("execution time=%ld\n", measure::execution([&foo, &x]() { foo.foo_member(x); })); 

Das heißt, ich habe eine Lambda-Funktion verwenden, um die Linie zusammengestellt zu bekommen.

Ich frage mich nur, warum der Compiler konnte es nicht für mich tun? Der Code Pfad ist genau definiert, wie die erste Version in eine Lambda-Funktion mit dem Capture-Mechanismus zu übersetzen. Wenn Sie erkennen, was ein moderner C++ Compiler mit dem Code zu tun wäre dies in der Tat sein, eine des einfachstenen Code Neuordnungs ...

Hinweis:

Es ist mit diesen gcc Fahnen zusammengestellt: -Wall -Werror -Wextra -std=c++11

+2

Warum würden Sie der Compiler erwarten, dass dies für Sie automatisch zu generieren? Eine andere Möglichkeit, dies zu erreichen, ist die Verwendung von 'std :: bind()'. –

+0

Einige Fragen, die sofort gestellt würden, wenn das erlaubt wäre: Was wäre der Typ von 'foo.foo_member'? Wäre es ein Compiler-generierter Typ? Würde das bedeuten, dass "foo.foo_member == foo.foo_member" scheitern würde und sich beschweren würde, dass der Vergleich zwei nicht verwandte Typen betrifft? Wenn ja warum? Wäre es nicht sinnvoll, 'auto x = y 'zuzulassen? foo.a: bar.a; '? Wie könnte das Feature neu gestaltet werden, um dies zu unterstützen? ... Und zurück zum Zeichenbrett. Dies ist wahrscheinlich nicht die Antwort, aber es ist nicht so einfach, wie Sie es vorschlagen. – hvd

+0

@ πάνταῥεῖ 'std :: bind()' benötigt eine andere Include-Datei. Die Lösung mit "Lambda" verwendet nur C++ - Code. Dies bedeutet für mich, dass der C++ - Compiler es ohne zusätzliche Informationen neu anordnen kann [in diesem Fall einschließlich der Überschrift 'functional'] –

Antwort

2

foo_member ist eine nicht statische Elementfunktion, daher wird ein implizites erstes Argument benötigt, der this Zeiger, den Sie übergeben müssen. Die richtige Syntax zum Erstellen eines Zeigers für die Elementfunktion ist &ClassName::Func, nicht class_instance.Func.

Ihrem Beispiel zu beheben, folgende Änderungen vornehmen:

Innerhalb main

printf("execution time=%ld\n", measure::execution(&Foo::foo_member, &foo, x)); 

Jetzt sind Sie einen Zeiger auf Foo::foo_member, und einen Zeiger auf Foo Instanz als erstes Argument übergeben. foo_member wird für diese Instanz aufgerufen.

measure::execution muss geändert werden, um Zeiger auf Elementfunktionen korrekt zu behandeln. Der einfachste Weg, dies zu tun, um wahrscheinlich std::experimental::apply zu verwenden.

std::experimental::apply(func, std::forward_as_tuple(std::forward<Args>(args)...)); 

Oder wenn Sie einen C++ 17-Compiler haben, können Sie verwenden std::invoke, in dem Fall, dass Sie den Anruf zu forward_as_tuple vermeiden können.

Live demo (mit std::experimental::apply)

Live demo (mit std::invoke)

+0

Ihre Lösung ändert auch die Ausführungsfunktion. Ich habe versucht, darauf hinzuweisen, dass nur der Aufruf - der die Fehlermeldung verursacht - mit C++ - Code umgeschrieben werden kann, ohne andere Teile zu berühren und aufgrund des genau definierten Code-Pfades könnte ein Compiler dies mit einigen aktivierten Befehlszeilenoptionen tun. –

+0

@AlBundy Dann verwenden Sie 'bind' oder ein Lambda, wie Sie gezeigt haben. Oder schreibe eine separate Überladung von 'execution', um Zeiger auf Elementfunktionen zu behandeln. – Praetorian

+0

Ich weiß, wie man das Problem löst. Ich war nur * laut * denkend, warum der Compiler das nicht für mich tun kann. –

0

Sie streiten für das Hinzufügen von völlig neue Syntax zu tun, was bereits in vielerlei Hinsicht getan werden kann.Die einfachste Lösung ist die Verwendung mem_fn:

measure::execution(std::mem_fn(&Foo::foo_member), foo, x) 

Sie auch std::bind oder boost::bind verwenden:

using std::placeholders; 
measure::execution(std::bind(&Foo::foo_member, foo, _1), x)