Soy bastante nuevo en C ++, y he intentado buscar una respuesta a esto y ejecutar pruebas, pero muchas veces tengo problemas para descubrir qué causa comportamientos específicos. Mi pregunta se relaciona con el uso de operadores [ ] anidados para acceder o modificar elementos en un bucle, por ejemplo:

//Declare

std::vector<int> a1 {10,20,30,40} ;
std::vector<int> a2 {2,3} ;
int S2 = a2.size() ; 

//Loop
for(int i = 0 ; i < S2 ; i++){
         a1[a2[i]] = a1[a2[i]] + 5000 ;
}

¿Esto se considera bien? Estoy preguntando no solo en términos de práctica común, sino también en términos de eficiencia y cualquier otro factor potencial que deba considerar. ¿Debo almacenar primero a[i] dentro de una variable temporal dentro del ciclo y luego usarlo para modificar mi elemento en el vector a2?

Sé que probablemente no sea la mejor estructura y debería usar otra estructura de datos para hacer este tipo de cosas, pero solo quiero entender si esto está bien o si podría causar un comportamiento indefinido.

1
dvd280 27 may. 2020 a las 09:31

4 respuestas

La mejor respuesta

Esto es perfectamente correcto.

Pero, de hecho, solo desea iterar los elementos de un contenedor estándar. C ++ permite el rango basado en la declaración para ese caso de uso:

for (index: a2) {
    a1[index] += 5000;
}

Me parece más legible incluso si es principalmente una cuestión de gustos ...

Descargo de responsabilidad: este código no controla la validez de los elementos de a2 como índice de a1.

5
Serge Ballesta 27 may. 2020 a las 06:51

A mí me parece bien. No es necesario crear una copia explícita de a2[i].

El único problema que veo con algo como esto es que el argumento dentro de [] debe ser del tipo std::size_t en lugar de int. Estos tipos de enteros abarcan diferentes rangos de valores, y aunque std::size_t es un tipo de entero sin signo, int es un entero con signo. Tenga cuidado con el uso de índices negativos o índices más allá del último elemento probablemente dará como resultado un comportamiento indefinido debido al acceso fuera de los límites. Pero si puede garantizar que los valores en a2 son siempre índices válidos para a1, entonces estos valores int se convertirán implícitamente en std::size_t y las cosas funcionan correctamente (lo que parece para ser el caso en el ejemplo de código en su pregunta).

También sugiero convertir la variable de bucle i a std::size_t (y usar ++i en lugar de i++ si quieres ser perfecto :).

En C ++ moderno, también puede usar un rango basado para no tener que usar una variable de índice explícito para acceder a los valores de a2:

for (auto indexFromA2 : a2)
    a1[indexFromA2] += 5000;

Esto es menos propenso a errores, ya que debe escribir menos lógica para administrar el acceso al elemento (y no tiene que detallar los tipos).

2
jotik 27 may. 2020 a las 06:56

Soy desarrollador de un software de cálculo de elementos finitos.

Utilizamos esta técnica para acceder a los valores dentro de un elemento. Nos ayuda a ahorrar mucha memoria

PERO: Tenga en cuenta que estropea su localidad de caché. No lo use en bucles pesados, si puede evitarlo.

Si necesita una verificación de rango y el rendimiento no es importante, puede considerar utilizar el operador at del std::vector

 for(const auto & index :a2) {
      a1.at(index) += 5000;
 }

La función at verifica automáticamente si n está dentro de los límites de elementos válidos en el vector, lanzando una excepción fuera del rango si no lo está (es decir, si n es mayor o igual que su tamaño). Esto está en contraste con el operador miembro [], que no verifica los límites.

Además, considere usar un bucle basado en rango

 //Loop
 for(const auto & index :a2) {
      a1[index] += 5000;
 }
6
schorsch312 27 may. 2020 a las 07:19

De alguna manera me aseguraría de que los elementos en a1 definidos en a2 realmente existan antes de intentar acceder a ellos, de lo contrario se quedará sin límites.

Pero en lo que respecta a [] anidado, esto está bien y no hay necesidad de crear otra copia de a2 para acceder a a1. El compilador simplemente está desenvolviendo tu expresión de adentro hacia afuera.

Todavía puedes simplificar un poco tu código

 //Declare
 std::vector<int> a1 {10,20,30,40} ;
 std::vector<int> a2 {2,3} ;

 //Loop
 for(int i = 0 ; i < a2.size() ; i++){
          if(a1.size()-1 < a2[i]){break;}
          a1[a2[i]] += 5000 ;
 }
1
jotik 27 may. 2020 a las 06:48