4 Stimmen

Weisen Sie abstrakte Funktoren der std::function zu – warum ist std::ref eine Lösung?

Ich möchte die Zuweisung von Funktoren an eine std::function in eine Methode kapseln. Anstatt eine std::function oder einen Zeiger auf eine std::function zu übergeben, muss ich Funktoren übergeben, die von einer gemeinsamen abstrakten Klasse Slot erben (d. h. diese Slots bieten zusätzliche Funktionalität).

Ich bin auf dieses Problem in einer anderen Form gestoßen hier. Zum Beispiel ist dort die Motivation, Zeiger von generischen Slots anstelle von std::functions zu verwenden, das Management der Lebensdauer der Funktoren.

Der folgende Code verdeutlicht das Problem. Sehen Sie die Methode assignFunctorPtr(...).

#include 
#include 

template
class Slot;

template
class Slot
{
    public:
        typedef R Ret_type;

    public:
        virtual ~Slot() {}
        virtual Ret_type operator()() = 0;
};

template
class Slot
{
    public:
        typedef R Ret_type;
        typedef A1 Arg1_type;

    public:
        virtual ~Slot() {}
        virtual Ret_type operator()(Arg1_type) = 0;
};

class TestSlot: public Slot
{
    public:
        void operator()(float& f)
        { std::cout << f ;}
};

template
class TestSignal
{
    public:
        typedef Slot Slot_type;

    std::function f;

    void assignFunctorPtr(Slot_type* slot_ptr)
    {
        //f = std::ref(*slot_ptr);   // A -> works!
        f = *slot_ptr;               // B -> compiler error!
    }
};

int main()
{
    TestSlot* slot = new TestSlot;
    TestSignal* signal = new TestSignal;

    signal->assignFunctorPtr(slot);
}

Dieser Code bricht ab, wenn Version B in assignFunctorPtr(...) verwendet wird.

Fehler: "error: cannot allocate an object of abstract type ‘Slot’
note:   because the following virtual functions are pure within ‘Slot’"

Und es wird kompiliert, wenn Version A in assignFunctorPtr(...) verwendet wird.

  • Warum wird es kompiliert, wenn std::ref verwendet wird, um den Funktor zu umschließen?
  • Welche spezifischen Anforderungen hat std::function an einen Funktor (siehe auch std::function reference)
  • Was wäre der richtige/beste Weg, dieses Problem zu lösen?
  • Ist es sicher, std::ref zu verwenden?

6voto

Björn Pollex Punkte 72424

std::function kopiert seine Argumente. Da das Objekt, das Sie zuweisen möchten, vom Basistyp ist (und eine reine virtuelle Memberfunktion hat), kann es nicht kopiert werden. Beachten Sie, dass es möglicherweise kopierbar wäre, wenn es keine reine virtuelle Memberfunktion hätte, aber Sie würden unter dem Slicing-Problem leiden.

Die Verwendung von std::ref ist sicher, solange Sie sicherstellen, dass das Objekt, an das std::ref gebunden ist, länger lebt als alle Verweise darauf.

Die eleganteste Lösung wäre meiner Meinung nach, assignFunctorPtr zu einer Funktionsvorlage zu machen, die ein Argument vom tatsächlichen Typ des Funktors (im Gegensatz zu einem Basistyp) akzeptiert. Wenn dieser kopierbar ist, würde die Zuweisung ohne std::ref funktionieren.

template
void assignFunctorPtr(SlotType* slot_ptr)
{
    f = *slot_ptr;               // funktioniert, wenn SlotType kopierbar ist
}

Ich glaube, diese Version würde auch funktionieren, wenn SlotType nur beweglich wäre, aber da könnte ich falsch liegen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X