2011-12-20 15 views
13

Desde el ++ 0x Wikipedia sitio c:Cómo escribir el for-loop basado en rango con Argv?

int my_array[5] = {1, 2, 3, 4, 5}; 
for (int &x : my_array) { 
    x *= 2; 
} 

Entonces, ¿por qué este código no funciona?

int main(int argc, char* argv[]) 
{ 
    for (char *arg : argv) 
    { 
     // Do something. 
    } 
} 

error:

main.cpp:36: error: no matching function for call to ‘begin(char**&)’ 

estoy usando Qt con g ++ 4.6.1 en Ubuntu 11.10.

Información adicional

Is There a Range Class in C++0x

Range-Based For-Loop Statement Definition Redundance

+0

Tenga en cuenta que (dependiendo, por supuesto, de la naturaleza de los posibles argumentos y el tipo de procesamiento que está tratando de hacer en ellos) utilizando un bucle for basado en rango en esta situación específica puede ser bastante limitante, porque estás obligado a procesar un argumento por iteración de bucle. El uso de un iterador explícito (o un puntero) le permite avanzar inmediatamente de un argumento al siguiente, a menudo la acción deseada al procesar argumentos como "-f miarchivo.out". Con un bucle for basado en rango, es probable que termines necesitando variables de estado explícitas o similares. – Jeremy

Respuesta

16

no lo hace, porque el sistema no puede decir cuánto tiempo es argv en tiempo de compilación. Es probable que alguien encuentre la sección adecuada del estándar para citarlo sobre esto.

Sin embargo, hay una forma de evitarlo, y eso es crear una clase personalizada para ajustar argv. Ni siquiera es tan difícil.

class argv_range { 
public: 
    argv_range(int argc, const char * const argv[]) 
     : argc_(argc), argv_(argv) 
    { 
    } 

    const char * const *begin() const { return argv_; } 
    const char * const *end() const { return argv_ + argc_; } 

private: 
    const int argc_; 
    const char * const *argv_; 
}; 

Así es como usted lo utiliza:

for (const char *arg: argv_range(argc, argv)) { 
    // Do something. 
} 

Sí, yo uso un montón de const s. Básicamente, argv es una matriz de punteros de caracteres, ninguno de los cuales debe modificarse, cada uno apuntando a una cadena, ninguno de los caracteres debe modificarse tampoco.

+0

Tienes razón, no es difícil, pero no pensé en el problema como lo hiciste: D Pensé que el nivel de indirección con mi 'char * arg' era incorrecto. Sí, muy lejos. Gracias por la propina y el código! – IAE

+0

@SoulBeaver: _nod_ El póster -1 creo que pensé lo mismo. Pero ese mensaje de error se debe a que el constructo ranged-for está tratando de encontrar la manera de calzar el tema de su "char carbon" con algo que sabe cómo manejar. El último recurso es intentar llamar 'being (thing)' y 'end (thing)' para usar algo parecido a un iterador. – Omnifarious

+1

@SoulBeaver: Aquí hay una buena respuesta SO que detalla los requisitos en los rangos que utiliza para bucles basados ​​en rangos: http://stackoverflow.com/questions/2648878/range-based-for-statement-definition-redundancy - Creo las definiciones predeterminadas de las funciones libres 'begin' y' end' intentan llamar a 'x.begin()' y 'x.end()' donde 'x' es su argumento (una referencia al rango). – Omnifarious

-1

argv es un puntero doble, argv **

Entonces, ¿dónde & X crea una referencia al elemento de la matriz, su * arg no crea una referencia, sino más bien es del mismo tipo que cada elemento de argv .

+0

-1: Esto es confuso, no es relevante para el problema que está teniendo el OP, y posiblemente es incorrecto. – Omnifarious

+0

¿Cómo no es relevante? Su sintaxis es incorrecta y es por eso que está teniendo errores de compilación. Los punteros y las referencias se confundieron. Cristo, las personas obtienen algunos puntos K y sienten la necesidad de evaluar sin motivo en SO hoy en día. –

+0

Excepto que su sintaxis no es incorrecta. Lo que está mal es que 'argv' no es un rango válido. – Omnifarious

18

Por lo general, lo primero que hago con argc y argv es la siguiente:

std::vector<std::string> arguments(argv, argv + argc); 

Ahora tengo un vector de cadenas de trabajar y que pueda usar fácilmente no sólo la de los bucles basados ​​en rango, pero también instalaciones de biblioteca estándar de C++.

código
for(std::string& s : arguments) { 
    // do stuff... 
} 

La Wikipedia funciona porque el tipo de my_array es una variable de tipo array. El código original no funciona, porque argv no es una matriz. La sintaxis char* argv[] puede hacer que parezca una matriz, pero eso es solo un triste artefacto de la sintaxis C. char* argv[] es exactamente lo mismo que char** argv. argv no es una matriz; en realidad es solo un puntero.

El bucle basado en la gama funciona en:

  • arrays;
  • cualquier tipo que tenga funciones de miembro begin() y end() que devuelvan iteradores;
  • cualquier tipo para el cual existe funciones que no son miembros begin y end que pueden ser llamados como begin(x) y end(x), con x ser lo que usted está interactuando sobre.

Como puede ver, los punteros no son parte de esta lista.

+0

+1: Eso también funciona. Aunque no es tan eficiente, tiene sus claras ventajas. – Omnifarious

+0

@ Fernandes: Este es mi procedimiento habitual también, pero para este programa ya sé qué tipo de entrada esperar, así que traté de evitar analizarlo en un vector y luego un segundo vector del tipo esperado. Solo quería procesar cada argumento entrante y luego convertirlo al tipo correcto antes de insertarlo en mi vector. – IAE

+0

@SoulBeaver, entonces su única opción es crear un tipo de rango, como Omnifarious mostró :) –

4

La solución de vector propuesta copia la matriz (solo los punteros, no las cadenas - pero aún así). Unnessary. La solución argv_range es lo que habría intentado, también, si quisiera forzar un ciclo basado en rango. Pero eso produce una gran cantidad de código adicional (admitido, solo una vez, si lo escribe en un archivo de encabezado y lo conserva, pero aún así).

El bucle clásico parece tan fácil para mí que me permito sólo para el post-it, no considero que vale la pena tener todo este esfuerzo sólo para tener un bucle basado gama ...

for (char** a = argv; *a; ++a) 
{ 
    // use *a, or if you don't like: 
    char* arg = *a; 
    // use arg... 
} 

o, si usted no necesitará siempre la matriz argv más tarde:

for (++argv; *argv; ++argv) 
{ 
    // use *argv, or if you don't like: 
    char* a = *argv; 
    // use a... 
} 

Hay una pequeña diferencia, usted puede haber notado: en la primera variante, que iterar sobre todos los valores, en el segundo , Dejo fuera el primero (que normalmente es el nombre del programa no estamos intere sted in en muchos casos). A la inversa, para cada uno:

for (char** a = argv + 1; *a; ++a); 
for (; *argv; ++argv); 

Esto se aplica únicamente, si utiliza std::vector<char*>; si usa std::vector<std::string>, como realmente se propone, ¡incluso las cadenas se copian!

+0

OK - antes de que grumbas - Sé que llego tarde. Solo quería agregar una alternativa para los lectores tardíos como yo ... – Aconcagua

+0

¡Simple pero perfecto! – youngwind

Cuestiones relacionadas