テンプレート関数がconst refを取得するメンバーへのポインター関数に対して機能しない


14

最近、私はいくつかのコードの繰り返しを解決するためにテンプレート関数を書きました。次のようになります。

template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr, const std::string& error, R (T::*fun)(Args...), Args... args) {
    if (auto sp = ptr.lock()) 
    {
        return std::invoke(fun, *sp, args...);
    }
    else 
    {
        throw std::runtime_error(error.c_str());
    }
}

int main() {
    auto a = std::make_shared<A>();
    call_or_throw(std::weak_ptr<A>(a), "err", &A::foo, 1);
}

このコードはclass A、次のように完全に機能します。

class A {
public:
    void foo(int x) {

    }
};

しかし、次のようなものをコンパイルできません:

class A {
public:
    void foo(const int& x) {

    }
};

なぜそうなのか(なぜそれが型を推定できないのか)と(もし可能であれば)このコードを参照で動作させるにはどうすればよいですか? 実例


多分Args&&...そしてstd::forward
FAS

@ user3365922が試してみました。解決策のように感じますが、機能しません
bartop

これこれは正しい方向にあなたを助けませんか?
ギズモ、

回答:


3

あなたの問題は、あなたが以下のArgs間で紛争の控除を持っているということです:

  • R (T::*fun)(Args...)
  • Args... args

私はより一般的なコード(R (T::*fun)(Args...)
constバージョンR (T::*fun)(Args...) constと他の代替の間の重複なし)を持つことをお勧めします:

template<class T, class F, class... Args>
decltype(auto) call_or_throw(const std::weak_ptr<T>& ptr,
                             const std::string& error,
                             F f,
                             Args&&... args)
{
    if (auto sp = ptr.lock()) 
    {
        return std::invoke(f, *sp, std::forward<Args>(args)...);
    }
    else 
    {
        throw std::runtime_error(error.c_str());
    }
}

メンバー関数のcv修飾についての良い点、これはこれまでのところ最良の解決策だと思います
bartop

8

Args型はconst&funパラメーター宣言から)およびargs宣言からの非参照の両方として推定できません。簡単な修正は、2つの別個のテンプレートタイプのパラメーターパックを使用することです。

template<class T, class R, class... Args, class... DeclaredArgs>
R call_or_throw(
    const std::weak_ptr<T>& ptr,
    const std::string& error,
    R (T::*fun)(DeclaredArgs...),
    Args... args);

欠点としては、使用法が悪い場合に少し長くなるエラーメッセージを想像できます。


1
あなたはおそらく欲しいArgs&&... args
Jarod42

5

テンプレートパラメータArgsのタイプはconst int&、3番目の関数引数&A::fooと同様intに推定され、4番目の関数パラメータと同様に推定されることに注意してください1。それらは一致せず、控除は失敗します。

あなたは控除から4番目のパラメータを除外することができます、例えば

template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr, 
                const std::string& error, 
                R (T::*fun)(Args...), 
                std::type_identity_t<Args>... args) {
//              ^^^^^^^^^^^^^^^^^^^^^^^^^^                

住む

PS:std::type_identityC ++ 20以降でサポートされています。しかし、それを実装するのは非常に簡単です。


1
それはどういうわけか完全転送で動作するつもりですか?
バートップ、

@bartopそうだと思います。4番目のパラメータを転送参照スタイルに適合させることができます。つまりArgs&&...、のstd::type_identityように3番目のパラメータに配置しますR (T::*fun)(std::type_identity_t<Args>...)LIVELIVE
songyuanyao

@songyuanyoええ、しかしそれは価値の議論のために壊れます。
バートップ、

コードのデモからforwardをすでに使用できます。それは単に「余分な」動きをします。
Jarod42
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.