2016-10-06 3 views
0

のは、私たちはクラスObjと、このメインを持っているとしましょう:は、C++でのパラメーターとしての機能を実行し

class Obj 
{ 
    public: 
    void func1(int n) {} 
    void func2(std:string n) {} 
}; 

std::vector<Obj> retrieveObjs() 
{ 
    std::vector<Obj> result; 
    // ... 
    return result; 
} 

int main() 
{ 
    // Call func1 for all obj 
    { 
     auto objs = retrieveObjs(); 
     for (auto& obj : objs)  
     { 
     obj.func1(100); 
     } 
    } 

    // Call func2 for all obj 
    { 
     auto objs = retrieveObjs(); 
     for (auto& obj : objs)  
     { 
     obj.func2("xxx"); 
     } 
    } 
    return 0; 
} 

私は次の擬似コードのように、すべてのOBJSから特定の機能を呼び出すための一般的な機能を持っていると思います。

void invokeAll(FUNCTION f, PARAM p) // pseudocode 
{ 
    auto objs = retrieveObjs(); 
    for (auto& obj : objs)  
    { 
    obj.f(p); 
    } 
} 

int main() // pseudocode 
{ 
    invokeAll(func1, 100); 
    invokeAll(func2, "xxx"); 
} 

私はこの仕事をするためにFUNCTIONPARAMを交換する方法がわかりません。

template/lambda/for_eachやこれに類する方法で可能ですか?

+0

はい、可能です。あなたが何かに遭遇した場合、それを試して、問題を取り返してください。あなたは、あなたの処分に関連した豊富な質問をここに持っています。 – Barry

+3

さて、['std :: function'](http://en.cppreference.com/w/cpp/utility/functional/function)と[' std :: bind'](http://en.cppreference。 com/w/cpp/utility/functional/bind)を参照してください。または['std :: mem_fn'](http://en.cppreference.com/w/cpp/utility/functional/mem_fn)。メンバ関数への簡単な古いポインタ。 –

+0

@JoachimPileborgあなたのコメントは実際答えの答えです。 – alexeykuzmin0

答えて

5

あなたは構文は次のようになりメンバ関数、へポインタのために良いユースケースで説明何:あなたのケースでは

// get the pointer to the function. 
auto funPtr = &Obj::func1; 

Obj obj; 

// call the method using the function pointer 
obj.(*funPtr)(); 

、あなたはパラメータとして関数ポインタを受け取ることができますし、引数としてパック。

// F is the type of the function pointer. 
// As arguments and return type of `f` can change, so it's type `F` can. 
template<typename F, typename... Args> 
void invokeAll(F f, Args... args) { 
    for (auto&& obj : retrieveObjs()) { 
     // We call member `f` with `obj` 
     // We expand the pack `args` to send it as multiple arguments 
     obj.(*f)(args...); 
    } 
} 

あなたが望んでいた同様の方法で関数を呼び出すことができます:std::invokeで、C++ 17では

// Notice the member function pointer syntax 
invokeAll(&Obj::func1, 100); 

// Work with multiple arguments, [100, "test"] will be packed into `args` 
invokeAll(&Obj::func2, 100, "test"); 

、あなたは可能にすることにより、さらにあなたのケースを一般化することができますパラメータとしてObjを取る関数のいずれかのタイプ:

template<typename F, typename... Args> 
void invokeAll(F f, Args... args) { 
    for (auto&& obj : retrieveObjs()) { 
     // invoke function `f` with `obj` as it's object and `args` as parameter. 
     std::invoke(f, obj, args...); 
    } 
} 

あなたワットの場合今アリは、ラムダを含む機能のより多くの種類をサポートする、あなたはvoid_tスタイルsfinaeを使用することができます。

// The compiler will pick this function if `obj.(*f)(args...)` can compile 
template<typename F, typename... Args> 
auto invokeAll(F f, Args... args) -> void_t<decltype(std::declval<Obj>().(*f)(args...))> { 
    //     Here's the constraint ------^ 
    for (auto&& obj : retrieveObjs()) { 
     obj.(*f)(args...); 
    } 
} 

// The compiler will pick this function if `f(obj, args...)` can compile 
template<typename F, typename... Args> 
auto invokeAll(F f, Args... args) -> void_t<decltype(f(std::declval<Obj>(), args...))> { 
    //     Here's the constraint ------^ 
    for (auto&& obj : retrieveObjs()) { 
     f(obj, args...); 
    } 
} 

void_tは、以下のように定義されます。

template<typename...> 
using void_t = void; 

すると、それに、あなたがこの構文のロックを解除しますあまりにも:あなたはあまりにも非コピー可能なタイプをサポートしたい

invokeAll([](Obj& obj, int a){ 
    // this block will be called for each `obj` in `retrieveObjs` 
}, 100); 

場合は、完璧な転送を探し

+0

本当に、私はちょうどラムダを取って、C++でメンバー関数ポインタを使うのではなく、呼び出し元に接着剤をさせるように誘惑されるでしょう – Yakk

+1

メンバー関数ポインタも使用したくないことを認めなければなりません。ラムダは至福です。しかし、メンバー関数ポインタはOPが望むものに最も近いものです。 –

1
template<class F, class R> 
void invoke_on_range(F&& f, R&& r) { 
    std::for_each(r.begin(), r.end(), std::forward<F>(f)); 
} 

これは範囲をとり、範囲の各要素でラムダを呼び出します。

int main() { 
    invoke_on_range([](Obj& obj){ obj.func1(100); }, retrieveObjs()); 
    invoke_on_range([](Obj& obj){ obj.func2("xxx"); }, retrieveObjs()); 
} 

ラムダを書き込むための定型文が少しありますが、構造は問題ではありません。


私は時々だけでなく、これは便利:

template<class F, class...Args> 
void invoke_on_each(F&& f, Args&&...args) { 
    using discard=int[]; 
    (void)discard{0,(void(
    f(std::forward<Args>(args)) 
),0)...}; 
} 

これは、ラムダfargs...のセットを取ります。それぞれargs...に1回、fを実行します。奇妙なdiscardのトリックは、すべて0の配列を作成し、...が望むものと正確に一致するコンテキストを生成するために、オプティマイザが実行しないように投げ捨てることを含みます。

retrieveObjsで操作しているという事実を隠すことは、別のラッピング機能を書く価値はないようですが、これも同様に行うことができます。

インターフェイスを実装から分割する場合は、class FF&&std::function<void(Obj&)>に置き換えて、パフォーマンスを適度なものにすることができます。

関連する問題