Die Vorlagenfunktion funktioniert nicht für die Zeiger-zu-Mitglied-Funktion unter const ref

14

In letzter Zeit habe ich eine Vorlagenfunktion geschrieben, um einige Code-Wiederholungen zu lösen. Es sieht aus wie das:

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);
}

Dieser Code funktioniert perfekt und class Asieht folgendermaßen aus:

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

    }
};

Aber kompiliert nicht für einen wie diesen:

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

    }
};

Warum ist es so (warum meine ich, warum der Typ nicht abgeleitet werden kann) und wie (wenn es überhaupt möglich ist) kann ich diesen Code mit Referenzen arbeiten lassen? Live Beispiel

Bartop
quelle
vielleicht Args&&...und std::forward?
Fas
@ user3365922 hat es versucht. Fühlt sich an wie eine Lösung, funktioniert nicht
Bartop
Wäre nicht diese und diese Hilfe , die Sie in die richtige Richtung?
Gizmo

Antworten:

3

Ihr Problem ist, dass Sie Konfliktabzüge haben für Args:

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

Ich schlage vor, allgemeineren Code (keine Duplikate zwischen R (T::*fun)(Args...)und
const-Version R (T::*fun)(Args...) constund andere Alternative) mit zu haben:

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());
    }
}
Jarod42
quelle
Guter Punkt über die Lebenslaufqualifizierung der Mitgliederfunktion, ich denke, dies ist die bisher beste Lösung
Bartop
8

ArgsTypen können nicht sowohl als const&(aus der funParameterdeklaration) als auch als Nichtreferenz aus der argsDeklaration abgeleitet werden. Eine einfache Lösung besteht darin, zwei separate Parameterpakete vom Vorlagentyp zu verwenden:

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);

Als Nachteil kann ich mir bei längerer Nutzung etwas längere Fehlermeldungen vorstellen.

LogicStuff
quelle
1
Sie wollen wahrscheinlichArgs&&... args
Jarod42
5

Beachten Sie, dass der ArgsTyp des Vorlagenparameters wie const int&beim 3. Funktionsargument &A::foound wie intbeim 4. Funktionsparameter abgeleitet wird 1. Sie stimmen nicht überein und führen dazu, dass der Abzug fehlschlägt.

Sie können den 4. Parameter vom Abzug ausschließen , z

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) {
//              ^^^^^^^^^^^^^^^^^^^^^^^^^^                

LEBEN

PS: std::type_identitywird seit C ++ 20 unterstützt; aber es ist ziemlich einfach, eine zu implementieren.

songyuanyao
quelle
1
Wird es irgendwie mit perfekter Weiterleitung funktionieren?
Bartop
@ Bartop Ich denke schon. Wir können den 4. Parameter so einstellen, dass er dem Weiterleitungsreferenzstil entspricht, dh Args&&...dann std::type_identityden 3. Parameter wie setzen R (T::*fun)(std::type_identity_t<Args>...). LIVE und LIVE
songyuanyao
@songyuanyo ja, aber dann wird es für Wert Argument brechen.
Bartop
Sie können bereits von Ihrem Code vorwärts Demo verwenden . Es wird nur "extra" bewegen.
Jarod42