恒常性に関係なくメソッドへのポインタを取る関数

Aug 19 2020

オブジェクトへの参照とそのメンバー関数へのポインターを受け取り、それを呼び出すジェネリック関数を実装したいと思います。ただし、クラスにconstメソッドとnon-constメソッドの両方がある場合、2つのオーバーロードを提供する必要があるため、これを行うことはできません。

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...))
{
    return (object.*method)(Us{}...);
}

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...) const)
{
    return (object.*method)(Us{}...);
}

constメソッドポインターとnon-constメソッドポインターの両方を受け入れるテンプレート関数を1つだけ記述する方法があるので、コードを2回記述する必要はありませんか?私はC ++ 14を使用しています。

より広い視野で、私が最終的に達成したいのは、メソッド引数が抽出されるデータバッファーである3番目のパラメーターを渡すことです。したがって、テンプレート関数はそれを可能な限り一般的に処理します。

回答

2 TedLyngmo Aug 19 2020 at 19:58

これは、を使用しないC ++ 14の代替手段std::functionです。

私が最終的に達成したいのは、メソッド引数が抽出されるデータバッファーである3番目のパラメーターを渡すことです。したがって、テンプレート関数はそれを可能な限り一般的に処理します。

コールサイトで使用するものは、ここに完全に転送されます。

template<typename Class, typename Func, typename... Args>
decltype(auto) callMethod_impl(Class& object, Func method, Args&&... args) {
    return (object.*method)(std::forward<Args>(args)...);
}


template<typename Ret, typename Class, typename... Us, typename... Args>
Ret callMethod(Class& object, Ret(Class::*method)(Us...), Args&&... args) {
    return callMethod_impl(object, method, std::forward<Args>(args)...);
}

template<typename Ret, typename Class, typename... Us, typename... Args>
Ret callMethod(const Class& object, Ret(Class::*method)(Us...) const, Args&&... args) {
    return callMethod_impl(object, method, std::forward<Args>(args)...);
}

デモ

あなたが必要な場合RetにはcallMethod_impl、単にテンプレートパラメータとして追加し、同じようにそれを呼び出すcallMethod_impl<Ret>(...)からcallMethod過負荷(デモ)。

3 JanSchultke Aug 19 2020 at 17:28

簡単に言うと、これを自分で実装しないでください。すでに次の形式で実装されていますstd::invoke。

#include <functional>

struct A {
    void foo(int x);
    void bar(int x) const;
};

void example() {
    A a;
    std::invoke(&A::foo, a, 3); 
    std::invoke(&A::bar, a, 3); 
}

振り返ってみると、C ++ 14タグを追加したことを確認すると、のドキュメントにstd::invokeは、プロジェクトで使用できるサンプル実装があります。

1 Jodocus Aug 19 2020 at 17:59

がないとstd::invoke、型消去に基づいて、多かれ少なかれ一般的な回避策しか実行できません。を使用std::functionすると、関数を同等に扱うプロキシ関数を作成できます。

#include <iostream>
#include <functional>

template<typename Ret, typename ...Us>
Ret callMethod_impl(std::function<Ret(Us...)> f) {
    // common implementation here
    return f(Us{}...);
}

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(Class &object, Ret (Class::*method)(Us...)) {
    return callMethod_impl(std::function<Ret(Us...)>(std::bind(method, object)));
}

template<typename Ret, typename Class, typename ...Us>
Ret callMethod(const Class &object, Ret (Class::*method)(Us...) const) {
    return callMethod_impl(std::function<Ret(Us...)>(std::bind(method, object)));
}

struct Foo {
    int bar() const { return 1; }
    float bar() { return 2.1f; };
};

int main() {
    Foo f;
    std::cout << callMethod(f, &Foo::bar) << std::endl;
}

ここではvolatile関数(+の組み合わせ)は扱われていませんが、完全な一般性ではなく、実用的なソリューションが必要な場合があることに注意してください。