Operatore binario di sottoscrizione / assegnazione in C ++

2

Stavo progettando una sorta di parser quando pensavo che sarebbe stato bello avere un overload di operatore come questo: operator[]=(subscipt_type s, rvalue_type val) . Ad esempio abbiamo una classe:

//collection of Foos
class FooCollection
{
public:

    //assume class Foo is defined
    Foo& operator[]=(int i, const Foo& val);
    //.....
};

E dobbiamo assegnare un valore di indice:

Foo value;
FooCollection c;
c[42] = value;

allora l'ultima riga di questo codice si risolverà in

c.operator[]=(42,value);

Il vantaggio di questo operatore rispetto a Foo& operator[](int i) esistente è che conosciamo il valore di assegnazione durante l'indice, quindi possiamo fare qualcosa che potrebbe non essere voluto in operator= ordinario.

Ad esempio, ho provato a progettare un albero di analisi composto da nodi polimorfici con un'interfaccia minima. Questa è la classe base del nodo, tutti gli altri tipi di nodo sono ereditati da esso:

class AnyNode
{
public:
    virtual AnyNode& operator=(const AnyNode& op)
    {
        //Takes almost no actions here.
        //Overloaded in each subclass to take some sensible actions
    }

    virtual AnyNode& operator[](int i)
    {
        //Returns special null value of AnyNode type.
    }
};

La classe AnyNode non è pura virtuale, quindi è possibile costruire AnyNode oggetti. Uno degli elementi di sintassi che cerco di analizzare è un elenco simile a Python, che può contenere valori eterogenei e può essere sottoscritto con un numero intero. Una classe per questo:

class ArrayNode : public AnyNode
{
public:
     AnyNode& operator[](int i)
    {
        //Returns a reference to i-th element
    }
};

Quindi cosa succederà quando proveremo ad assegnare qualcosa alla cella dell'array, che non è stata assegnata prima e il suo tipo concreto non è stato definito? AnyNode& operator=(const AnyNode& op) da class AnyNode verrà chiamato e nessuna azione verrà eseguita. Ma voglio sapere il AnyNode& rvalue subito dopo l'iscrizione, in modo che sia possibile dedurne il tipo concreto con RTTI e prendere delle azioni sensate. L'operatore di pedici / assegnazioni potrebbe assomigliare a questo:

AnyNode& operator[]=(int i, const AnyNode& val)
{
    //AnyNode::getType() is provided and it returns a value of the predefined enum.
    //stor is an underlying container that holds pointers to AnyNode, 
    //assume it's possible to reference stor[i].
    //NodeNumber is another subclass of AnyNode.
    switch(val->getType())
    {
    case nodetypeNumber:
        stor[i] = new NodeNumber();//Also we may need to delete stor[i] before new allocation.
        //Process further specific initialization of stor[i].
        break;
    //................
    }
    //Or we could use dynamic_cast instead of switch(val->getType())
}

Suppongo che un tale tipo di operatore di sottoscrizione / assegnazione possa essere facilmente e coerentemente integrato nel linguaggio. Inoltre, delimita le parti di codice che vengono eseguite quando l'espressione subscript è un lvalue e quando è un valore rvalue: con esso il solito operatore di pedice dovrebbe essere chiamato solo quando l'abbonamento è rvalore. Ovviamente, se l'operatore proposto non è definito, ma è definita una operator[] , il codice dovrebbe essere eseguito in un modo usuale.

Quindi le mie domande sono: quali insidie potrei mancarmi di questo operatore e perché non è incluso nella lingua? Può rompere la coerenza linguistica in qualsiasi modo o può far emergere qualsiasi ambiguità nella lingua?

    
posta Sergey 16.06.2014 - 23:05
fonte

1 risposta

2

Questo operatore composito non fa parte del linguaggio C ++ perché nessuno ha mai presentato un caso adatto per includerlo.

L'insieme di operatori supportati da C ++ è strongmente influenzato dall'insieme di operazioni che è possibile eseguire sui tipi di base. Va così lontano che tutti i simboli operatore in C ++ hanno un significato per almeno alcuni dei tipi primitivi.
Il problema con il tuo operator[]= proposto è che, per i tipi primitivi, non ha un significato più specifico di una semplice contrazione del% già esistenteoperator[] e operator= .

Quando si aggiunge qualcosa a C ++, occorre fare un attento equilibrio tra i costi di aggiunta della feature (nella complessità del compilatore) e i vantaggi di essa. In particolare, una funzionalità sarà aggiunta solo se rende possibile qualcosa che non era possibile in precedenza, o se rende molto più semplice fare qualcosa, e c'è una grande richiesta per quella funzionalità.
Ad essere onesti, non vedo nuovi operatori, o un meccanismo che fornisca sovraccarichi per le composizioni di operatori, essendo stato aggiunto a C ++ in qualsiasi momento.

Gli effetti del tuo operator[]= possono anche essere ottenuti usando una classe proxy, come questa:

class ArrayNode : public AnyNode
{
    class Proxy 
    {
        SomeArrayLikeType& stor;
        int i;
    public:
        Proxy& operator=(const AnyNode& val)
        {
            switch(val->getType())
            {
            case nodetypeNumber:
                stor[i] = new NodeNumber();//Also we may need to delete stor[i] before new allocation.
                //Process further specific initialization of stor[i].
                break;
            //................
            }
            return *this;
        }

        const AnyNode& operator AnyNode() {
            return stor[i];
        }
    };

public:
    Proxy operator[](int i) {
        return Proxy(stor, i);
    }
};
    
risposta data 17.06.2014 - 11:35
fonte

Leggi altre domande sui tag