HOWTO · C++

Sobrecargue el operador corchete en C++

Aprenda a sobrecargar el operador[] de C++ con acceso mutable y const, comprobaciones de índice opcionales y un ejemplo de estructura de datos personalizada.

En esta página

Para sobrecargar operator[] en C++, decláralo como una función miembro que acepte un índice. Devuelve una referencia si quieres que se pueda asignar mediante objeto[indice]; añade una sobrecarga const para los objetos de solo lectura. Así, un tipo personalizado ofrece un acceso similar al de un array y mantiene el control sobre su almacenamiento y sus reglas de acceso.

Por qué y cuándo sobrecargar operator[]

La sobrecarga de operadores define cómo funciona un operador con un tipo definido por el usuario, como una clase. Puede hacer que un contenedor o una matriz resulte más natural de usar: en lugar de llamar a un accesor independiente, se puede escribir elementos[indice]. Una sobrecarga de clase solo afecta a las expresiones que involucran esa clase; no puede cambiar el funcionamiento de [] cuando sus operandos son únicamente tipos integrados.

La sobrecarga es una función miembro llamada operator[]. Declárala en la sección public si quienes usan la clase necesitan acceder a ella. El tipo del parámetro puede ajustarse al modelo de indexación, por ejemplo, std::size_t para un índice no negativo de un contenedor. El tipo de retorno determina qué se puede hacer con el elemento seleccionado.

Implementar operator[] para leer y escribir

Este ejemplo completo almacena tres enteros inicializados. La sobrecarga modificable devuelve int&, por lo que una asignación actualiza el elemento seleccionado. La sobrecarga const devuelve const int&, así que una lectura a través de un objeto constante no puede modificar el elemento.

#include <array>
#include <cstddef>
#include <iostream>

class IntList {
private:
    std::array<int, 3> values{10, 20, 30};

public:
    int& operator[](std::size_t index) {
        return values[index];
    }

    const int& operator[](std::size_t index) const {
        return values[index];
    }
};

int main() {
    IntList items;
    items[1] = 25;

    const IntList& view = items;
    std::cout << items[1] << ' ' << view[2] << '\n';
}
25 30

El programa imprime 25 30. En la primera expresión se selecciona la sobrecarga no constante, y la referencia que devuelve permite asignar un valor. El objeto constante selecciona la sobrecarga constante. Devolver el elemento por valor permitiría leerlo, pero una asignación solo cambiaría el valor temporal, no el elemento almacenado.

Permitir el acceso constante y gestionar índices no válidos

El ejemplo anterior usa std::array::operator[], que requiere un índice dentro del rango válido del array. La indexación sin comprobación no verifica los límites: usar un índice fuera de rango produce un comportamiento indefinido. La misma regla se aplica si una sobrecarga personalizada indexa directamente un array o contenedor subyacente sin comprobaciones.

Si la clase debe comprobar los índices, añade una comprobación explícita antes de acceder al almacenamiento y lanza std::out_of_range cuando el índice quede fuera del rango admitido. Incluye <stdexcept> para usar ese tipo. Es una decisión de diseño de la clase; operator[] no ofrece esta comprobación automáticamente. En un contenedor que siga la división habitual de la biblioteca estándar, operator[] puede seguir sin comprobar los límites y otro accesor de comprobación puede validar el índice.

Aplicar operator[] a una estructura de datos personalizada

Una matriz puede admitir matriz[fila][columna] si su primer operator[] devuelve un objeto que represente una fila, como std::vector<int>&. El segundo [] pertenece entonces a ese objeto fila. Para una matriz constante, proporciona una sobrecarga constante que devuelva const std::vector<int>&, de modo que tanto la fila como sus elementos sean de solo lectura.

Cada paso de una indexación encadenada debe devolver un valor que admita la siguiente operación []. La referencia a la fila también permite que una expresión como matriz[fila][columna] = valor llegue al elemento almacenado. La clase de matriz debe definir su propia política de índices: el acceso sin comprobaciones a filas y columnas requiere índices válidos; también se pueden añadir comprobaciones explícitas en cualquiera de los dos pasos de indexación.