#include <iostream>
#include <vector>

struct vector_seguro : std::vector<int> {       // seremos como std::vector<int>, pero nuestro operator[] realmente funcionará como .at (que valida el índice y lanza una excepción si es inválido)
   using std::vector<int>::vector;              // hereder también los constructores (esto no ocurre por defecto)

   int& operator[](int i) {                     // redefinir nuestro el operator[] para terminar llamando a la función .at heredada de vector
      return at(i);
   }

   const int& operator[](int i) const {         // redefinir nuestro el operator[] para vectores const
      return at(i);
   }
};

int main( ) {
   vector_seguro v = { 3, 1, 4, 1, 6 };

   int indice;
   std::cin >> indice;                          // correr el programa probando usar un índice válido y un índice inválido

   try {
      std::cout << v[indice] << "\n";
      std::cout << "todo bien\n";
   } catch (const std::exception& ex) {
      std::cout << ex.what( );
   }
}
