2016-07-11 3 views
0

関数の実行時間を測定する方法が必要でした。私はSO https://stackoverflow.com/a/21995693/3179492でこの非常に良い答えを見つけました。これは完璧なソリューションです。非静的メンバー関数の無効な使用のためにコンパイラが回避できない理由

バリデーションパラメータリストを持つ関数ポインタを使用しています。ここで

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

foo_memberが静的​​ではないので、このコードはコンパイルされません。エラーメッセージはinvalid use of non-static member functionです。

問題を解決する方法はいくつかあります。私にとっては、最もエレガントな、最短かつ効果的な方法はこれです:

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

このことは、私がコンパイル行を取得するためにラムダ関数を使用します。

なぜ私はコンパイラがそれをできないのだろうと思っていますか?コードパスは正確に定義されています最初のバージョンをキャプチャメカニズムを使用してラムダ関数に変換します。あなただけの現代的なC++コンパイラがコードでやっている、これは実際にを並べ替える最も簡単なコードの一つであるものを実現すると...

注:-Wall -Werror -Wextra -std=c++11

それは、これらのgccフラグでコンパイルされました

+2

あなたは、コンパイラがあなたのためにこれを自動的に生成することを期待するのはなぜ?これを実現する別の方法は単に 'std :: bind()'を使うことです。 –

+0

これが許されていればすぐに出てくるいくつかの疑問: 'foo.foo_member'の型は何でしょうか?それはコンパイラ生成の型ですか?これは、 'foo.foo_member == foo.foo_member'が失敗するということは、比較に2つの無関係な型が含まれていると不平を言いますか?もしそうなら、なぜですか? 'auto x = y 'を許可するのは意味がありませんか? foo.a:bar.a; '?どのように機能を再設計してそれをサポートすることができましたか? ...そして、絵画ボードに戻って。これはおそらく答えではありませんが、あなたが提案するほどシンプルではありません。 – hvd

+0

@πάνταῥεῖ 'std :: bind()'は別のインクルードファイルを必要とします。 'lambda 'の解決法は、C++コードのみを使用します。これは私のために、C++コンパイラは追加の情報(この場合、ヘッダ 'functional'を含む)を使わずに再配置することができます。 –

答えて

2

foo_memberは静的でないメンバー関数なので、暗黙の最初の引数、つまりポインタを渡す必要があります。メンバ関数へのポインタを作成するための適切な構文は、ではなく、&ClassName::Funcです。今、あなたはFoo::foo_memberへのポインタを渡している

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

mainの中で、最初の引数としてFooインスタンスへのポインタ:

には、以下の変更を行い、あなたの例を修正するには。このインスタンスに対して foo_memberが呼び出されます。

measure::executionは、メンバー関数へのポインタを正しく処理するために変更する必要があります。これを行う最も簡単な方法はおそらくstd::experimental::applyです。

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

それとも、C++ 17のコンパイラを持っている場合、あなたはforward_as_tupleへの呼び出しを回避することができ、その場合にはstd::invokeを、使用することができます。

Live demostd::experimental::apply付き)

Live demostd::invoke付き)

+0

あなたのソリューションは 'execution'関数も変更します。私は、エラーメッセージの原因となる呼び出しだけが、他の部分に触れることなくC++コードで書き直すことができ、コードパスが正確に定義されているため、コンパイラはいくつかのコマンドラインオプションを有効にして実行できることを指摘しました。 –

+0

@AlBundy次に、あなたが示したように 'bind'かlambdaを使います。あるいは、メンバ関数へのポインタを扱う 'execution'の別のオーバーロードを書いてください。 – Praetorian

+0

私はこの問題を解決する方法を知っています。私はちょうど*大声で*コンパイラが私のためにそれをすることができない理由を考えていました。 –

0

あなたはすでに多くの方法で行うことができます何をすべきか、完全に新しい構文を追加することを主張しています。最も簡単な解決策はmem_fnを使用することです:

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

またstd::bindまたはboost::bindを使用することができます。

using std::placeholders; 
measure::execution(std::bind(&Foo::foo_member, foo, _1), x) 
関連する問題