46

オブザーバーの特定のメンバー関数にパラメーターを完全に転送できるオブザーバー パターンを再作成しようとしています。

複数のオーバーライドを持つメンバー関数のアドレスを渡そうとすると、引数に基づいて正しいメンバー関数を推測できません。

#include <iostream>
#include <vector>
#include <algorithm>

template<typename Class>
struct observer_list
{
    template<typename Ret, typename... Args, typename... UArgs>
    void call(Ret (Class::*func)(Args...), UArgs&&... args)
    {
        for (auto obj : _observers)
        {
            (obj->*func)(std::forward<UArgs>(args)...);
        }
    }
    std::vector<Class*> _observers;
};

struct foo
{
    void func(const std::string& s)
    {
        std::cout << this << ": " << s << std::endl;
    }
    void func(const double d)
    {
        std::cout << this << ": " << d << std::endl;
    }
};

int main()
{
    observer_list<foo> l;
    foo f1, f2;
    l._observers = { &f1, &f2 };

    l.call(&foo::func, "hello");
    l.call(&foo::func, 0.5);

    return 0;
}

これは でコンパイルできませんtemplate argument deduction/substitution failed

関数シグネチャの型と同じ型である必要はありませんが、その型に変換できるパラメーターを渡すことができる必要があるためArgs...、 andがあったことに注意してください。UArgs...

あいまいさを解消するために呼び出しを使用できるとstd::enable_if<std::is_convertible<Args, UArgs>>考えていましたが、可変個引数テンプレート パラメーター パックでこれを実行できるとは思いませんか?

ここでテンプレート引数の推論を機能させるにはどうすればよいですか?

4

1 に答える 1

51

問題はここにあります:

l.call(&foo::func, "hello");
l.call(&foo::func, 0.5);

両方の行について、コンパイラはどちらfoo::funcを参照しているのかわかりません。したがって、欠落している型情報 (つまり、 の型foo:func) をキャストを介して提供することによって、自分自身を明確にする必要があります。

l.call(static_cast<void (foo::*)(const std::string&)>(&foo::func), "hello");
l.call(static_cast<void (foo::*)(const double      )>(&foo::func), 0.5);

または、コンパイラが推測できず、 の型を定義するテンプレート引数を指定できますfunc

l.call<void, const std::string&>(&foo::func, "hello");
l.call<void, double            >(&foo::func, 0.5);

double上記ではなく andを使用する必要があることに注意してくださいconst double。その理由は、一般にdoubleconst doubleは 2 つの異なるタイプだからです。ただし、 と が同じ型であるかのように見なされる状況が 1 つありdoubleますconst double。それは、関数の引数としてです。例えば、

void bar(const double);
void bar(double);

2 つの異なるオーバーロードではありませんが、実際には同じ関数です。

于 2013-07-26T07:19:41.350 に答える