1

非同期のオブザーバーパターンを作成しようとすると、コンパイラエラーC3867が発生します。これを解決する方法がわかりません。サンプルコードスニペットは次のとおりです

class Subject;

class Observer
{
public:
    virtual void notify(Subject* s) = 0;
    virtual ~Observer() {};
};

class Subject
{
    std::map<std::string, Observer *> observers;
protected:
    void notify_observers()
    {
        std::map<std::string, Observer *>::iterator iter;
        for (iter = observers.begin(); iter != observers.end(); ++iter) {
            void (Observer::*notify)(Subject *) = iter->second->notify;
            std::async(std::launch::async, notify, this);
        }
    }

public:
    virtual ~Subject() {};
    void observer(std::string id, Observer* o)
    {
        observers[id] = o;
    }
};

template<typename Iter, typename type>
class Sort : public Observer {
public:
    virtual void notify(Subject* s)
    {
        TestSort<Iter> *a;
        a = dynamic_cast<TestSort<Iter> *>(s);
        std::vector<type> temp(a->beg(), a->end());

        sort(temp->beg(), temp->end());
    }
};

template<typename Iter, typename type>
class InsertionSort : public Sort<Iter, type>
{
    void sort(Iter beg, Iter end) {
        for (Iter i = beg; i != end; ++i)
            std::rotate(std::upper_bound(beg, i, *i), i, i+1);
    }


};
int main ()
{
    std::vector<double> data(100);
    std::generate(data.begin(), data.end(), [](){return rand() % 500;} ); 
    auto ts = TestSort<std::vector<double>::iterator >(data.begin(), data.end());

    auto is = new InsertionSort<std::vector<double>::iterator, double >();
    //.................
    ts.observer("InsertionSort", is);
    //.........................
    ts.Triggerd();
    return 0;
}

エラーはわかりますが

 error C3867: 'Observer::notify': function call missing argument list; use '&Observer::notify' to create a pointer to member

しかし、この文脈では、私はそれを解決する方法を理解することはできません。

このコンテキストでは、notifyが、ではなく、単に追加可能なメンバー関数であった場合

void (Observer::*notify)(Subject *) = iter->second->notify;

私は単に書くことができた

void (Observer::*notify)(Subject *) = &Observer::notify;

しかし、notifyはポリモーフィック関数であり、コンパイル時に適切な関数に対処できません。

どのように処理すればよいか提案してください

4

2 に答える 2

4

通常の仮想関数呼び出しで適切な関数を把握する必要がないのと同様に、コンパイル時に適切な関数を把握する必要はありません。を使用するだけ&Observer::notifyです。正しい機能は、アドレスを取得するときではなく、呼び出し時に選択されます。

于 2012-12-18T18:08:01.453 に答える
1

変化する:

void (Observer::*notify)(Subject *) = iter->second->notify;
std::async(std::launch::async, notify, this);

に:

void (Observer::*notify)(Subject *) = &Observer::notify;
std::async(std::launch::async, std::mem_fun(notify), iter->second, this);

メソッドを呼び出すときは、インスタンスへのポインタと引数の両方が必要です。標準の構文はですがrettype retval = instance->method(arg);std::mem_fun次のように使用できるファンクターを返します。これにより、メンバー関数に渡されるrettype retval = std::mem_fun(&InstanceType::method)(instance, arg);暗黙のポインターが明示的になります。this

メソッドへのポインタとvirtualオブジェクトポインタから、メソッドstd::mem_funのどのインスタンスをvirtual呼び出す必要があるかを判断できます。

bind同様のことは、またはラムダで行うことができます。ラムダ構文を使用したほぼ同等の呼び出しは次のとおりです。

 Observer* observer = iter->second;
 std::async(std::launch::async, [observer,this]() { observer->notify(this); } );

以下のコメントを参照してください:あなたは使用する必要はありませんstd::mem_funasyncあなたのためにそれをします。それでも、次の引数としてメンバー関数のインスタンスポインタを渡す必要があります。

于 2012-12-18T18:49:36.753 に答える