2011-01-23 8 views
5

Considere este código (VS2008):Goto antes de la inicialización variable causa de error del compilador

void WordManager::formatWords(std::string const& document) 
{ 
    document_ = document; 
    unsigned int currentLineNo = 1; 

    size_t oldEndOfLine = 0; 
    size_t endOfLine = document_.find('\n'); 
    while(endOfLine != std::string::npos) 
    { 
     std::string line = document_.substr(oldEndOfLine, (endOfLine - oldEndOfLine)); 
     if(line.size() < 2) 
     { 
      oldEndOfLine = endOfLine + 1; 
      endOfLine = document_.find('\n', oldEndOfLine); 
      continue; 
     } 

     std::vector<std::string> words = Utility::split(line); 
     for(unsigned int i(0); i < words.size(); ++i) 
     { 
      if(words[i].size() < 2) 
       continue; 
      Utility::trim(words[i], WordManager::delims); 
      Utility::normalize(words[i], WordManager::replace, WordManager::replaceWith); 

      if(ruleOne(words[i]) && ruleTwo(words[i])) 
      { 
       std::set<Word>::iterator sWIter(words_.find(Word(words[i]))); 

       if(sWIter == words_.end()) 
        words_.insert(Word(words[i])).first->addLineNo(currentLineNo); 
       else 
        sWIter->addLineNo(currentLineNo); 
      } 
     } 
     ++currentLineNo; 

     oldEndOfLine = endOfLine + 1; 
     endOfLine = document_.find('\n', oldEndOfLine); 
    } 
} 

Si es importante: se trata de un código de asignación de la preparación se usa para filtrar y modificar palabras en un documento. document sostiene el documento (leer previamente de archivo)

Deseo introducir un Goto maliciosa, porque creo que en realidad es más limpio, en este caso, así:

void WordManager::formatWords(std::string const& document) 
{ 
    document_ = document; 
    unsigned int currentLineNo = 1; 

    size_t oldEndOfLine = 0; 
    size_t endOfLine = document_.find('\n'); 
    while(endOfLine != std::string::npos) 
    { 
     std::string line = document_.substr(oldEndOfLine, (endOfLine - oldEndOfLine)); 
     // HERE!!!!!! 
     if(line.size() < 2) 
      goto SkipAndRestart; 

     std::vector<std::string> words = Utility::split(line); 
     for(unsigned int i(0); i < words.size(); ++i) 
     { 
      if(words[i].size() < 2) 
       continue; 
      Utility::trim(words[i], WordManager::delims); 
      Utility::normalize(words[i], WordManager::replace, WordManager::replaceWith); 

      if(ruleOne(words[i]) && ruleTwo(words[i])) 
      { 
       std::set<Word>::iterator sWIter(words_.find(Word(words[i]))); 

       if(sWIter == words_.end()) 
        words_.insert(Word(words[i])).first->addLineNo(currentLineNo); 
       else 
        sWIter->addLineNo(currentLineNo); 
      } 
     } 

SkipAndRestart: 
     ++currentLineNo; 

     oldEndOfLine = endOfLine + 1; 
     endOfLine = document_.find('\n', oldEndOfLine); 
    } 
} 

Si es o no es una elección buen diseño es irrelevante en este momento. El compilador se queja error C2362: initialization of 'words' is skipped by 'goto SkipAndRestart'

No entiendo este error. ¿Por qué es importante, y es un error, que se omita la inicialización de las palabras? Eso es exactamente lo que quiero que suceda, no quiero que haga más trabajo, solo reinicie el bucle sangriento. ¿La macro continua no hace más o menos exactamente lo mismo?

+0

Hubiera pensado que esto solo sería una advertencia, no un error. ¿Qué sucede si solo usas 'break' en lugar de goto? –

+5

¡La mayoría de la gente probablemente no estaría de acuerdo con que la versión 'goto' sea" más limpia "! –

+0

@Oli: Lo sé, por eso dije que el diseño real de la cosa es irrelevante; No quiero comenzar una guerra de llama: P @Paul: compila. – IAE

Respuesta

13

Usted está saltando sobre la construcción de la matriz words:

if(line.size() < 2) 
     goto SkipAndRestart; 
    std::vector<std::string> words = Utility::split(line); 
    // ... 
SkipAndRestart: 

Usted podría ha usado words después de la etiqueta SkipAndRestart:, lo que habría sido un problema. No lo utiliza en su caso, pero la variable words no se destruirá hasta el extremo del ámbito en el que se introduce, por lo que, en lo que respecta al compilador, la variable todavía está en uso en el punto de la etiqueta.

Usted puede evitar esto poniendo words dentro de su propio ámbito:

if(line.size() < 2) 
     goto SkipAndRestart; 
    { 
     std::vector<std::string> words = Utility::split(line); 
     // ... 
    } 
SkipAndRestart: 

Tenga en cuenta que la declaración continue salta al final del bucle, en un punto en el que realmente no puede poner una etiqueta . Este es un punto después de la destrucción de cualquier variable local dentro del ciclo, pero antes del salto hacia atrás hasta la parte superior del ciclo.

+0

Mi pregunta estaba más orientada a por qué esto es un problema real. – IAE

+1

Acabo de agregar un poco sobre el momento de la destrucción que ayudará a explicar eso. –

+0

¡Muchas gracias! ^^ – IAE

2

Con eso goto, va a omitir la línea:

std::vector<std::string> words = Utility::split(line); 

Esto no es sólo saltarse la re-initilisation, se salta la creación. Debido a que esa variable se define dentro del ciclo, se crea recientemente cada iteración del ciclo. Si te saltas esa creación, no puedes usarla.

Si lo quiere crear una vez, debe mover esa línea fuera del ciclo.

me abstendré de mi primera inclinación, que es para decirle que el uso de goto y cleaner en la misma frase es generalmente mal - hay situaciones en las que goto es mejor pero no estoy seguro de que este es uno de ellos. Lo que le diré es que, si esto es tarea, goto es una mala idea: cualquier educador desaprobará el uso de goto.

+0

La tarea ya está entregada. Solo estoy experimentando aquí. Mi profesor HACE fruncir el ceño y me acusa de ocultar un buen código de C++. – IAE

1

Como siempre cuando alguien piensa goto sería más legible el código, refactorización a utilizar (inline) funciona es por lo menos tan bueno y sin goto: tratar de entender lo que el bucle interno

// Beware, brain-compiled code ahead! 
inline void WordManager::giveThisAMeaningfulName(const std::string& word, std::string__size_type currentLineNo) 
{ 
    Utility::trim(word, WordManager::delims); 
    Utility::normalize(word, WordManager::replace, WordManager::replaceWith); 
    if(ruleOne(word) && ruleTwo(word)) 
    { 
     std::set<Word>::iterator sWIter(words_.find(Word(word))); 
     if(sWIter == words_.end()) 
      words_.insert(Word(word)).first->addLineNo(currentLineNo); 
     else 
      sWIter->addLineNo(currentLineNo); 
    } 
} 

void WordManager::formatWords(std::string const& document) 
{ 
    document_ = document; 
    std::string__size_type currentLineNo = 1; 

    std::string line; 
    while(std::getline(line)) 
     if(line.size() > 1) 
     { 
      std::vector<std::string> words = Utility::split(line); 
      if(word.size() > 1) 
       for(unsigned int i(0); i < words.size(); ++i) 
        giveThisAMeaningfulName(words[i], currentLineNo++); 
     } 
} 

no me he molestado lo hace, así que dejo que le dé un nombre significativo. Tenga en cuenta que, una vez que le ha dado un nombre, no necesito entender su algoritmo para saber qué hace, porque el nombre lo dice todo).

Tenga en cuenta que he reemplazado su línea manuscrita -extracción por std::getline(), que hizo el código aún más pequeño.

+0

Esto es interesante. ¡Personalmente pensé que mi código es muy legible y fácil de entender! Lo que ha escrito es una opción válida, pero sin entender la semántica de la clase, ha estropeado algunas partes de la función. Aunque eso es culpa mía, solo sirve para mostrar que necesito escribir un código aún más legible :) – IAE

+1

@SoulBeaver: no puse demasiado esfuerzo en comprender esto realmente y supe por qué agregué esa exención de responsabilidad en la parte superior. ':)' De todos modos, lo que traté de transmitir es esto: 'goto' es una forma difícil de entender y desestructurada para saltar, mientras que existen varias alternativas estructuradas fáciles de entender:' return', 'break' , 'si' ... Siempre que tengas la intención de usar' goto', mira la división del código en cuestión para poder usar esos en su lugar. Al usarlos, debía condensar el código hasta el punto en que SO ya no agregaba una barra de desplazamiento vertical. Y cuanto menos código, más fácil es de entender. – sbi

+0

Estoy programando en C++ durante casi veinte años y nunca he usado 'goto'. Aún no he encontrado un caso en el que mi código esté más claro que otras opciones de refactorización. A saber, la capacidad de C++ para no pagar por las funciones (al incluir en línea), lo que me permite tomar fragmentos de código casi arbitrarios y ponerle un nombre (el nombre de la función en la que los pongo), que es mucho mejor que comentar, además del control de dependencia explícito que esto tiene (tiene que pasar los datos requeridos explícitamente a esas funciones, y si son demasiados, le hace pensar que lo que está haciendo mal) es una herramienta muy valiosa. – sbi

Cuestiones relacionadas