1

Here is a simple example that reproduces the problem I have:

class Base
{
public:
    Base() {}
    Base(int value) {}
    Base& operator[](int index) { return Base(); }
    Base& operator[](const char* index) { return Base(); }
};

class IntChild : public Base
{
    Base& operator[](const char* index) = delete;
};

IntChild test;
test[4] = 1; //Error: no operator "[]" matches these operands

In the Base class two operators[] are implemented. The operator taking a const char* as argument shall be deleted in the child class. But when the operator[](const char*) is deleted in the child class, an error occures also when using the operator taking an integer:

no operator "[]" matches these operands

I am using Visual Studio 2019.

What am I missing?

rkgghz
  • 456
  • 8
  • 19

1 Answers1

1

Add using Base::operator[]; in the derived class to bring it into the scope:

struct Base
{
    Base() = default;
    Base(int value) {}
    Base& operator[](int index) { return *this; }
    Base& operator[](const char* index) { return *this; }
};

struct IntChild : public Base
{
    using Base::operator[];
    Base& operator[](const char* index) = delete;
};

IntChild test;
test[4] = 1; //works

Here is the relevant excerpt from using-declaration, which explains why after adding the using you can call the int-overload in the base class:

In class definition

Using-declaration introduces a member of a base class into the derived class definition, such as to expose a protected member of base as public member of derived. In this case, nested-name-specifier must name a base class of the one being defined. If the name is the name of an overloaded member function of the base class, all base class member functions with that name are introduced. If the derived class already has a member with the same name, parameter list, and qualifications, the derived class member hides or overrides (doesn't conflict with) the member that is introduced from the base class.

The question why it doesn't fall back to the base-class operator[](int) without the using is answered by unqualified name lookup (especially the point b) below):

Class definition

For a name used anywhere in class definition (including base class specifiers and nested class definitions), except inside a member function body, a default argument of a member function, exception specification of a member function, or default member initializer, where the member may belong to a nested class whose definition is in the body of the enclosing class, the following scopes are searched:

a) the body of the class in which the name is used until the point of use

b) the entire body of its base class(es), recursing into their bases when no declarations are found

So you don't go on to search for functions in the base class once you found one declaration in the derived class. Note that this also holds when you implement the derived class (not only when you =delete it).

davidhigh
  • 14,652
  • 2
  • 44
  • 75
  • OK so I guess when using operator[] on a child class without its own operator[] implemention, the base class operator[] is used. But when the operator[](const char*) is deleted in the child class, it cannot fall back on the base operator[] and therefore using is neccessary. Right? – rkgghz May 15 '23 at 10:48
  • @rkgghz: I've added the functionality as described in the cppreference to the answer. – davidhigh May 15 '23 at 11:09
  • @rkgghz: this is C++, and every little thing gets complex: I've added another passage. – davidhigh May 15 '23 at 11:24