C++ Subscript and Assignment Binary Operator Standards

cstandards

I was designing a kind of a parser when I thought that it would be nice to have an operator overload like this: operator[]=(subscipt_type s, rvalue_type val). For example we have a class:

//collection of Foos
class FooCollection
{
public:

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

And we need to assign a subscripted value:

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

then the last line of this code will resolve into

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

The advantage of this operator in comparison to existing Foo& operator[](int i) is that we know the rvalue of assignment during the subscripting, so we can do something that could be unwished in ordinary operator=.

For example, I tried to design a parse tree consisting of polymorphic nodes with minimal interface. This is the basic node class, all other node types are inherited from it:

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

The AnyNode class is not pure virtual, so it's possible to construct AnyNode objects. One of the syntax elements that I try to parse is a python-like list, which may contain heterogenous values and may be subscripted with an integer. A class for it:

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

So what will happen when we try to assign something to the array cell, which was not assigned before and it's concrete type was not defined? AnyNode& operator=(const AnyNode& op) from class AnyNode will be called and no actions will be taken. But I want to know the AnyNode& rvalue just after subscripting, so that it could be possible to deduce it's concrete type with RTTI and take some sensible actions. Subscript/assignment operator could look like this:

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

I suppose, such kind of subscipt/assignment operator can be easily and consistently built into language. Moreover, it delimits the pieces of code which execute when the subscripted expression is an lvalue and when it's an rvalue: with it the usual subscript operator should be called only when the subscription is rvalue. Of course, if the proposed operator is not defined, but a operator[] is defined, the code should execute in a usual way.

So my questions are: what pitfalls could I miss about this operator and why does it not included into the language?
Can it break language consistency in any way or can it bring up any ambiguity into the language?

Best Answer

This composite operator isn't part of the C++ language because nobody has ever made a proper case for including it.

The set of operators supported by C++ is very strongly influenced by the set of operations that you can do on the basic types. It goes so far that all operator symbols in C++ have a meaning for at least some of the primitive types.
The problem with your proposed operator[]= is that, for the primitive types, it doesn't have a more specific meaning than a simple contraction of the already existing operator[] and operator=.

When adding anything to C++, a careful balance must be made between the costs of adding the feature (in compiler complexity) and the benefits of it. In particular, a feature will only be added if it makes something possible that wasn't previously possible, or if it makes it vastly easier to do something, and there is a large demand for that feature.
To be honest, I don't see new operators, or a mechanism to provide overloads for compositions of operators, being added to C++ anytime soon.


The effects of your operator[]= can be obtained as well by using a proxy class, like this:

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