Por que variáveis ​​não podem ser declaradas em uma instrução switch?


945

Eu sempre me perguntei isso - por que você não pode declarar variáveis ​​após um rótulo de caso em uma instrução switch? Em C ++, você pode declarar variáveis ​​praticamente em qualquer lugar (e declará-las próximas ao primeiro uso é obviamente uma coisa boa), mas o seguinte ainda não funcionará:

switch (val)  
{  
case VAL:  
  // This won't work
  int newVal = 42;  
  break;
case ANOTHER_VAL:  
  ...
  break;
}  

O texto acima fornece o seguinte erro (MSC):

a inicialização de 'newVal' é ignorada pelo rótulo 'case'

Isso também parece ser uma limitação em outros idiomas. Por que isso é um problema?


10
Para obter uma explicação baseada na gramática C BNF, consulte stackoverflow.com/questions/1180550/weird-switch-error-in-obj-c/...
Johne

Aqui está uma leitura muito boa sobre instruções e rótulos de chave (ABC :) em geral.
Etherealone

4
Eu diria 'Por que variáveis ​​não podem ser inicializadas em uma instrução switch em vez de declaradas'. Desde que apenas declarar a variável, apenas um aviso no MSVC é dado.
ZoomIn

Respostas:


1144

Casedeclarações são apenas rótulos . Isso significa que o compilador interpretará isso como um salto diretamente para o rótulo. Em C ++, o problema aqui é de escopo. Seus colchetes definem o escopo como tudo dentro da switchinstrução. Isso significa que você terá um escopo no qual um salto será executado ainda mais no código, ignorando a inicialização.

A maneira correta de lidar com isso é definir um escopo específico para essa caseinstrução e definir sua variável nela:

switch (val)
{   
case VAL:  
{
  // This will work
  int newVal = 42;  
  break;
}
case ANOTHER_VAL:  
...
break;
}

94
Relativo à abertura de um novo escopo - favorece a legibilidade e a consistência no código. Antigamente, você pode ter automaticamente um quadro de pilha "extra", mas agora esse não deve ser o caso de nenhum compilador de otimização decente.
Alto Jeff

10
Eu concordo com Jeff - é muito fácil "assumir" o escopo ao ler uma instrução switch por causa do estilo de recuo que a maioria das pessoas usa. Meu próprio estilo é sempre abrir um novo escopo para cada caso / padrão, se tiver mais de uma linha.
Licitações

39
workmad3 - Você pode encontrar algum compilador C ++ que gere um novo quadro de pilha se você não declarar novas variáveis? Você me preocupou brevemente, mas nada do G ++ 3.1, Visual C ++ 7 ou Intel C ++ 8 irá gerar qualquer código para novos escopos nos quais você não declara nenhuma variável.
7119 Chris Jefferson

10
@ workmad3 inserindo um novo bloco de chaves não causa um novo quadro de pilha stackoverflow.com/questions/2759371/…
MTVS

3
@TallJef Não sei a que 'velhos tempos' você se refere. Eu nunca encontrei um compilador em que todo o espaço de pilha de um método não seja alocado quando o método é inserido, em 40 anos.
Marquês de Lorne

333

Esta questão é originalmente foi etiquetado como [C] e [C ++], ao mesmo tempo. O código original é realmente inválido em C e C ++, mas por razões não relacionadas completamente diferentes.

  • No C ++, esse código é inválido porque o case ANOTHER_VAL:rótulo salta para o escopo da variável newValignorando sua inicialização. Saltos que ignoram a inicialização de objetos automáticos são ilegais no C ++. Este lado do problema é abordado corretamente pela maioria das respostas.

  • No entanto, na linguagem C, ignorar a inicialização de variáveis ​​não é um erro. Saltar para o escopo de uma variável sobre sua inicialização é legal em C. Isso significa simplesmente que a variável é deixada não inicializada. O código original não é compilado em C por um motivo completamente diferente. A etiqueta case VAL:no código original é anexada à declaração da variável newVal. Na linguagem C, as declarações não são declarações. Eles não podem ser rotulados. E é isso que causa o erro quando esse código é interpretado como código C.

    switch (val)  
    {  
    case VAL:             /* <- C error is here */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:     /* <- C++ error is here */
      ...
      break;
    }

A adição de um {}bloco extra corrige os problemas de C ++ e C, mesmo que esses problemas sejam muito diferentes. No lado do C ++, ele restringe o escopo de newVal, certificando-se de que case ANOTHER_VAL:não entra mais nesse escopo, o que elimina o problema do C ++. No lado C, extra {}introduz uma declaração composta, fazendo com que o case VAL:rótulo se aplique a uma declaração, o que elimina a emissão de C.

  • No caso C, o problema pode ser facilmente resolvido sem o {}. Basta adicionar uma declaração vazia após o case VAL:rótulo e o código se tornará válido

    switch (val)  
    {  
    case VAL:;            /* Now it works in C! */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:  
      ...
      break;
    }

    Observe que, embora agora seja válido do ponto de vista de C, ele permanece inválido do ponto de vista de C ++.

  • Simetricamente, no caso de C ++, o problema pode ser facilmente resolvido sem o {}. Apenas remova o inicializador da declaração da variável e o código se tornará válido

    switch (val)  
    {  
    case VAL: 
      int newVal;
      newVal = 42;  
      break;
    case ANOTHER_VAL:     /* Now it works in C++! */
      ...
      break;
    }

    Observe que, embora agora seja válido do ponto de vista do C ++, ele permanece inválido do ponto de vista do C.


4
@ Ant: Eu entendo por que aquele que corrige C ++ não é aplicável a C; no entanto, não consigo entender como ele corrige o problema do C ++ de ignorar a inicialização em primeiro lugar? Ainda não pularia a declaração e a atribuição de newValquando salta ANOTHER_VAL?
legends2k

13
@ legends2k: Sim, ainda o ignora. No entanto, quando digo "corrige o problema", quero dizer que corrige o erro do compilador C ++ . No C ++, é ilegal pular uma declaração escalar com o inicializador , mas é perfeitamente adequado pular uma declaração escalar sem o inicializador . No case ANOTHER_VAL:ponto, a variável newValé visível, mas com valor indeterminado.
AnT

3
Fascinante. Encontrei essa pergunta depois de ler a §A9.3: Compound StatementK&R C (segunda edição). A entrada mencionou a definição técnica de uma declaração composta que é {declaration-list[opt] statement-list[opt]}. Confuso, porque pensei que uma declaração ERA uma declaração, procurei-a e imediatamente encontrei esta pergunta, um exemplo em que a disparidade dita se torna aparente e realmente quebra um programa. Acredito que outra solução (para C) seria colocar outra declaração (possivelmente uma declaração nula?) Antes da declaração para que a declaração rotulada seja satisfeita.
Braden Best

Ops, acabei de notar que a solução de declaração nula que sugeri já está em sua resposta. Deixa pra lá então.
Braden Best

3
Vale a pena notar que a correção de adicionar uma instrução vazia funciona apenas para o C99 em diante. Em C89, as variáveis ​​devem ser declaradas no início de seu bloco anexo.
Arthur Tacca

136

Está bem. Só para esclarecer isso, nada tem a ver com a declaração. Diz respeito apenas a "pular a inicialização" (ISO C ++ '03 6.7 / 3)

Muitas postagens aqui mencionaram que pular a declaração pode resultar na variável "não sendo declarada". Isso não é verdade. Um objeto POD pode ser declarado sem um inicializador, mas terá um valor indeterminado. Por exemplo:

switch (i)
{
   case 0:
     int j; // 'j' has indeterminate value
     j = 0; // 'j' initialized to 0, but this statement
            // is jumped when 'i == 1'
     break;
   case 1:
     ++j;   // 'j' is in scope here - but it has an indeterminate value
     break;
}

Onde o objeto é um não-POD ou agregado, o compilador adiciona implicitamente um inicializador e, portanto, não é possível pular essa declaração:

class A {
public:
  A ();
};

switch (i)  // Error - jumping over initialization of 'A'
{
   case 0:
     A j;   // Compiler implicitly calls default constructor
     break;
   case 1:
     break;
}

Essa limitação não está limitada à instrução switch. Também é um erro usar 'goto' para pular uma inicialização:

goto LABEL;    // Error jumping over initialization
int j = 0; 
LABEL:
  ;

Um pouco de trivialidade é que essa é uma diferença entre C ++ e C. Em C, não é um erro pular a inicialização.

Como outros já mencionaram, a solução é adicionar um bloco aninhado para que o tempo de vida da variável seja limitado ao rótulo de caso individual.


2
"Erro ao pular a inicialização" ??? Não com o meu GCC. Pode dar um aviso "j pode ser usado unitizado" ao usar j abaixo da etiqueta, mas não há erro. No entanto, no caso de mudança, há um erro (um erro grave, não um aviso fraco).
Mecki

9
@ Mecki: É ilegal em C ++. ISO C ++ '03 - 6.7 / 3: "... Um programa que salta de um ponto em que uma variável local com duração de armazenamento automático não está no escopo para um ponto em que está no escopo está mal formada, a menos que a variável tenha o tipo POD (3.9) e é declarado sem um inicializador (8.5). "
Richard Corden

1
Sim, mas não é ilegal em C (pelo menos o gcc diz que não é). j será não inicializado (possui algum número aleatório), mas o compilador o compila. No entanto, no caso da instrução switch, o compilador nem sequer a compila e não vejo a diferença entre um caso de goto / label e um caso de switch.
Mecki

8
@ Mecki: Em geral, o comportamento de um único compilador não reflete necessariamente o que é realmente permitido pelo idioma. Eu verifiquei C'90 e C'99 e ambos os padrões incluem um exemplo com um salto sobre a inicialização em uma instrução switch.
Richard Corden

38

Toda a instrução switch está no mesmo escopo. Para contornar isso, faça o seguinte:

switch (val)
{
    case VAL:
    {
        // This **will** work
        int newVal = 42;
    }
    break;

    case ANOTHER_VAL:
      ...
    break;
}

Observe os colchetes.


30

Depois de ler todas as respostas e mais algumas pesquisas, recebo algumas coisas.

Case statements are only 'labels'

Em C, de acordo com a especificação,

§6.8.1 Declarações rotuladas:

labeled-statement:
    identifier : statement
    case constant-expression : statement
    default : statement

Em C, não há nenhuma cláusula que permita uma "declaração rotulada". Simplesmente não faz parte do idioma.

assim

case 1: int x=10;
        printf(" x is %d",x);
break;

Isso não será compilado , consulte http://codepad.org/YiyLQTYw . O GCC está dando um erro:

label can only be a part of statement and declaration is not a statement

Até

  case 1: int x;
          x=10;
            printf(" x is %d",x);
    break;

isso também não está sendo compilado , consulte http://codepad.org/BXnRD3bu . Aqui também estou recebendo o mesmo erro.


Em C ++, de acordo com a especificação,

A declaração etiquetada é permitida, mas a inicialização rotulada não é permitida.

Veja http://codepad.org/ZmQ0IyDG .


A solução para essa condição é dois

  1. Use o novo escopo usando {}

    case 1:
           {
               int x=10;
               printf(" x is %d", x);
           }
    break;
  2. Ou use uma declaração fictícia com etiqueta

    case 1: ;
               int x=10;
               printf(" x is %d",x);
    break;
  3. Declare a variável antes de switch () e inicialize-a com valores diferentes na instrução case, se ela atender aos seus requisitos

    main()
    {
        int x;   // Declare before
        switch(a)
        {
        case 1: x=10;
            break;
    
        case 2: x=20;
            break;
        }
    }

Mais algumas coisas com a instrução switch

Nunca escreva nenhuma declaração no comutador que não faça parte de nenhum rótulo, porque elas nunca serão executadas:

switch(a)
{
    printf("This will never print"); // This will never executed

    case 1:
        printf(" 1");
        break;

    default:
        break;
}

Veja http://codepad.org/PA1quYX3 .


2
Você descreveu corretamente o problema C. Mas a afirmação de que na inicialização rotulada em C ++ não é permitida não é completamente verdadeira. Não há nada errado com a inicialização rotulada em C ++. O que o C ++ não permite é pular a inicialização da variável ano escopo da variável a. Portanto, do ponto de vista de C, o problema está no case VAL:rótulo e você o descreveu corretamente. Mas do ponto de vista do C ++, o problema está no case ANOTHER_VAL:rótulo.
AnT

No C ++, ao contrário do C, as declarações são um subconjunto de instruções.
93016 Keith Thompson

20

Você não pode fazer isso, porque os caserótulos são na verdade apenas pontos de entrada no bloco que o contém.

Isso é mais claramente ilustrado pelo dispositivo de Duff . Aqui está um código da Wikipedia:

strcpy(char *to, char *from, size_t count) {
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}

Observe como os caserótulos ignoram totalmente os limites do bloco. Sim, isso é mau. Mas é por isso que seu exemplo de código não funciona. Saltar para um caserótulo é o mesmo que usar goto, portanto, você não pode saltar sobre uma variável local com um construtor.

Como vários outros pôsteres indicaram, você precisa colocar um bloco de sua preferência:

switch (...) {
    case FOO: {
        MyObject x(...);
        ...
        break; 
    }
    ...
 }

1
A implementação do dispositivo deste Duff possui um bug que o torna extremamente lento: count é do tipo int, portanto o% deve executar uma operação real de divisão / módulo. Faça a contagem sem assinatura (ou, melhor ainda, sempre use size_t para contagens / índices) e o problema desaparece.
R .. GitHub Pare de ajudar o gelo

1
@R ..: O que ?! No sistema de complemento de dois, a assinatura não afeta os módulos por potências de 2 (é apenas um AND nos bits inferiores) e não afeta as divisões por potências de 2, desde que a arquitetura do processador possua uma operação aritmética à direita ( SARem x86, versus o SHRque é para turnos não assinados).
Chris Jester-Young

@ Chris: Eu acredito que ele quer dizer quando o compilador deve permitir valores negativos onde "apenas um AND nos bits inferiores" não se mantém; por exemplo, -1% 8 fornece -1 no sistema de complemento desses dois usando g ++ (neste caso, a implementação é definida por 5.6 / 4).

3
@ Chris: Eu concordo com você que R está exagerando o impacto; Eu só vi o seu comentário e sabia que um simples E não era suficiente.

1
Também digno de nota é o código original da Wikipedia para enviar dados para uma saída mapeada na memória, o que parece estranho aqui porque não é mencionado e cada byte é copiado para o mesmo local "para". É possível contornar isso adicionando o postfix ++ ao to ou mencionando o caso de uso para E / S mapeada na memória. Totalmente periférico à pergunta original :-).
Peter

16

A maioria das respostas até agora estão erradas em um aspecto: você pode declarar variáveis ​​após a instrução case, mas não pode inicializá-las:

case 1:
    int x; // Works
    int y = 0; // Error, initialization is skipped by case
    break;
case 2:
    ...

Como mencionado anteriormente, uma boa maneira de contornar isso é usar chaves para criar um escopo para o seu caso.


1
Sr. 32, você não entendeu qual é o seu erro: sim, isso não será compilado, mas não porque você está declarando uma variável dentro de um comutador. O erro ocorre porque você está tentando declarar uma variável depois de uma declaração, que é ilegal em C.
MrZebra

1
Agora, um dia que é legal em C90 e a versão mais recente do c
Jeegar Patel

12

Meu truque favorito de troca de mal é usar um if (0) para pular um rótulo de caixa indesejado.

switch(val)
{
case 0:
// Do something
if (0) {
case 1:
// Do something else
}
case 2:
// Do something in all cases
}

Mas muito mal.


Muito agradável. Exemplo de por: caso 0 e caso uma força, por exemplo, inicializar uma variável de forma diferente que é em seguida utilizado no caso 2.
hlovdal

1
Se você deseja que o caso 0 e o caso 1 passem pelo caso 2. (sem o caso 0 se refira ao caso 1). Não sei se é realmente útil, mas com certeza funciona.
Petruza

1
Você pode simplesmente pular para o rótulo necessário gotosem
ocultar

10

Tente o seguinte:

switch (val)
{
    case VAL:
    {
        int newVal = 42;
    }
    break;
}

7

Você pode declarar variáveis ​​em uma instrução switch se iniciar um novo bloco:

switch (thing)
{ 
  case A:
  {
    int i = 0;  // Completely legal
  }
  break;
}

O motivo está relacionado à alocação (e recuperação) de espaço na pilha para armazenamento das variáveis ​​locais.


1
A variável pode ser declarada, mas não pode ser inicializada. Além disso, tenho certeza de que o problema não está relacionado à pilha e às variáveis ​​locais.
Richard Corden

6

Considerar:

switch(val)
{
case VAL:
   int newVal = 42;
default:
   int newVal = 23;
}

Na ausência de instruções de interrupção, às vezes o newVal é declarado duas vezes e você não sabe se o faz até o tempo de execução. Meu palpite é que a limitação se deve a esse tipo de confusão. Qual seria o escopo do newVal? A Convenção ditaria que seria o bloco inteiro do interruptor (entre os aparelhos).

Eu não sou programador C ++, mas em C:

switch(val) {
    int x;
    case VAL:
        x=1;
}

Funciona bem. Declarar uma variável dentro de um bloco de chave é bom. Declarar após uma guarda de caso não é.


3
@ Mr.32: na verdade, seu exemplo mostra que um printf não é executado, mas, neste caso, o int x não é uma declaração, mas uma declaração, o x é declarado, o espaço é reservado toda vez que o ambiente de funções é empilhado, Veja: codepad.org/4E9Zuz1e
Petruza

Eu esperava encontrar isso ao ler o título da pergunta, porque a pergunta não é sobre declarar variáveis ​​dentro dos rótulos "case:", mas nas instruções do switch. E somente você (e VictorH, enfatizando sua resposta) realmente falou sobre variáveis ​​nas declarações de chave.
CESSS

4

A seção inteira do switch é um único contexto de declaração. Você não pode declarar uma variável em uma declaração de caso como essa. Tente isso:

switch (val)  
{  
case VAL:
{
  // This will work
  int newVal = 42;
  break;
}
case ANOTHER_VAL:  
  ...
  break;
}

A variável pode ser declarada, mas não pode ser inicializada.
Richard Corden

@ Richard Corden Estou confiante de que a inicialização funcionará. Você ainda afirma que não pode ser inicializado?
chux - Restabelece Monica 13/09

3

Se o seu código diz "int newVal = 42", você esperaria razoavelmente que o newVal nunca seja não inicializado. Mas se você passar por cima dessa declaração (que é o que você está fazendo), é exatamente isso que acontece - newVal está no escopo, mas não foi atribuído.

Se é isso que você realmente queria que acontecesse, o idioma exige que seja explicitado dizendo "int newVal; newVal = 42;". Caso contrário, você pode limitar o escopo de newVal ao caso único, o que é mais provável do que você queria.

Pode esclarecer as coisas se você considerar o mesmo exemplo, mas com "const int newVal = 42;"


3

Eu só queria enfatizar o ponto de Slim . Uma construção de switch cria um escopo completo para o cidadão de primeira classe. Portanto, é possível declarar (e inicializar) uma variável em uma instrução switch antes do primeiro rótulo do caso, sem um par de colchetes adicional:

switch (val) {  
  /* This *will* work, even in C89 */
  int newVal = 42;  
case VAL:
  newVal = 1984; 
  break;
case ANOTHER_VAL:  
  newVal = 2001;
  break;
}

-1 aqui int newVal = 42; nunca será executado. veja este codepad.org/PA1quYX3
Jeegar Patel

4
a declaração int newVal será executada, mas não a = 42atribuição.
Petruza

3

Até agora, as respostas foram para C ++.

Para C ++, você não pode pular uma inicialização. Você pode em C. No entanto, em C, uma declaração não é uma declaração e os rótulos de caso devem ser seguidos por declarações.

Então, C válido (mas feio), C ++ inválido

switch (something)
{
  case 1:; // Ugly hack empty statement
    int i = 6;
    do_stuff_with_i(i);
    break;
  case 2:
    do_something();
    break;
  default:
    get_a_life();
}

Por outro lado, em C ++, uma declaração é uma declaração, portanto, o seguinte é C ++ válido, C inválido

switch (something)
{
  case 1:
    do_something();
    break;
  case 2:
    int i = 12;
    do_something_else();
}

1
O segundo exemplo NÃO é C ++ válido (teste com vc2010 e gcc 4.6.1 C ++ não permite ignorar a parte de inicialização. A mensagem de erro do gcc é: inicialização cruzada de 'int i'
zhaorufei

3

Interessante que isso seja bom:

switch (i)  
{  
case 0:  
    int j;  
    j = 7;  
    break;  

case 1:  
    break;
}

... mas isso não é:

switch (i)  
{  
case 0:  
    int j = 7;  
    break;  

case 1:  
    break;
}

Entendo que uma correção é bastante simples, mas ainda não estou entendendo por que o primeiro exemplo não incomoda o compilador. Como foi mencionado anteriormente (2 anos antes hehe), a declaração não é o que causa o erro, apesar da lógica. Inicialização é o problema. Se a variável for inicializada e declarada nas diferentes linhas, ela será compilada.


1
Primeiro, não está bom no gcc 4.2: "erro: expressão esperada antes de 'int'". Como Peter e Mr.32 dizem, "caso 0:; int j; ..." e "caso 0:; int j = 7; ..." funcionam ambos. O problema em C é apenas que "case <label>: declaração" não é uma sintaxe C válida.
dubiousjim

3

Eu escrevi esta resposta original para esta pergunta . No entanto, quando terminei, descobri que a resposta estava fechada. Então, eu publiquei aqui, talvez alguém que goste de referências ao padrão ache isso útil.

Código original em questão:

int i;
i = 2;
switch(i)
{
    case 1: 
        int k;
        break;
    case 2:
        k = 1;
        cout<<k<<endl;
        break;
}

Na verdade, existem 2 perguntas:

1. Por que posso declarar uma variável após o caserótulo?

É porque no rótulo C ++ deve estar no formato:

N3337 6.1 / 1

declaração rotulada:

...

  • atributo-especificador-seqopt case constant-expression :statement

...

E na C++ declaração, a declaração também é considerada como declaração (em oposição a C):

N3337 6/1:

declaração :

...

declaração-declaração

...

2. Por que posso pular a declaração de variável e depois usá-la?

Porque: N3337 6,7 / 3

É possível transferir para um bloco, mas não de uma maneira que ignore as declarações com a inicialização . Um programa que salta (A transferência da condição de uma instrução switch para um rótulo de caso é considerada um salto a esse respeito.)

de um ponto em que uma variável com duração de armazenamento automática não está no escopo a um ponto em que está no escopo está mal formada , a menos que a variável tenha tipo escalar , tipo de classe com um construtor padrão trivial e um destruidor trivial, uma versão qualificada para cv de um desses tipos ou uma matriz de um dos tipos anteriores e é declarado sem um inicializador (8.5).

Como ké do tipo escalar e não é inicializado no ponto da declaração, é possível saltar sobre sua declaração. Isso é semanticamente equivalente:

goto label;

int x;

label:
cout << x << endl;

No entanto, isso não seria possível, se xfoi inicializado no ponto da declaração:

 goto label;

    int x = 58; //error, jumping over declaration with initialization

    label:
    cout << x << endl;

1

Novas variáveis ​​podem ser decalcadas apenas no escopo do bloco. Você precisa escrever algo como isto:

case VAL:  
  // This will work
  {
  int newVal = 42;  
  }
  break;

Obviamente, o newVal só tem escopo dentro do aparelho ...

Cheers, Ralph


1

Um switchbloco não é o mesmo que uma sucessão de if/else ifblocos.Estou surpreso que nenhuma outra resposta explique claramente.

Considere esta switchdeclaração:

switch (value) {
    case 1:
        int a = 10;
        break;
    case 2:
        int a = 20;
        break;
}

Pode ser surpreendente, mas o compilador não o verá como simples if/else if. Ele produzirá o seguinte código:

if (value == 1)
    goto label_1;
else if (value == 2)
    goto label_2;
else
    goto label_end;

{
label_1:
    int a = 10;
    goto label_end;
label_2:
    int a = 20; // Already declared !
    goto label_end;
}

label_end:
    // The code after the switch block

As caseinstruções são convertidas em rótulos e depois chamadas com goto. Os colchetes criam um novo escopo e é fácil ver agora por que você não pode declarar duas variáveis ​​com o mesmo nome em umswitch bloco.

Pode parecer estranho, mas é necessário oferecer suporte a falhas (ou seja, não usar breakpara permitir que a execução continue na próxima case).


0

Acredito que o problema em questão é que a declaração foi ignorada e você tentou usar o var em outro lugar, não seria declarado.


0

newVal existe em todo o escopo do comutador, mas só é inicializado se o membro VAL for atingido. Se você criar um bloco ao redor do código em VAL, deverá estar OK.


0

O padrão C ++ possui: É possível transferir para um bloco, mas não de maneira a ignorar as declarações com a inicialização. Um programa que salta de um ponto em que uma variável local com duração de armazenamento automática não está no escopo para um ponto em que está no escopo está mal formada, a menos que a variável tenha o tipo POD (3.9) e seja declarada sem um inicializador (8.5).

O código para ilustrar esta regra:

#include <iostream>

using namespace std;

class X {
  public:
    X() 
    {
     cout << "constructor" << endl;
    }
    ~X() 
    {
     cout << "destructor" << endl;
    }
};

template <class type>
void ill_formed()
{
  goto lx;
ly:
  type a;
lx:
  goto ly;
}

template <class type>
void ok()
{
ly:
  type a;
lx:
  goto ly;
}

void test_class()
{
  ok<X>();
  // compile error
  ill_formed<X>();
}

void test_scalar() 
{
  ok<int>();
  ill_formed<int>();
}

int main(int argc, const char *argv[]) 
{
  return 0;
}

O código para mostrar o efeito inicializador:

#include <iostream>

using namespace std;

int test1()
{
  int i = 0;
  // There jumps fo "case 1" and "case 2"
  switch(i) {
    case 1:
      // Compile error because of the initializer
      int r = 1; 
      break;
    case 2:
      break;
  };
}

void test2()
{
  int i = 2;
  switch(i) {
    case 1:
      int r;
      r= 1; 
      break;
    case 2:
      cout << "r: " << r << endl;
      break;
  };
}

int main(int argc, const char *argv[]) 
{
  test1();
  test2();
  return 0;
}

0

Parece que objetos anônimos podem ser declarados ou criados em uma instrução de caso de opção pelo motivo de não poderem ser referenciados e, como tal, não passarem para o próximo caso. Considere este exemplo de compilação no GCC 4.5.3 e no Visual Studio 2008 (pode ser um problema de conformidade, por isso os especialistas devem avaliar)

#include <cstdlib>

struct Foo{};

int main()
{
    int i = 42;

    switch( i )
    {
    case 42:
        Foo();  // Apparently valid
        break;

    default:
        break;
    }
    return EXIT_SUCCESS;
}

Se você quiser votar, explique o motivo. Estou curioso para saber por que a criação de um objeto anônimo parece ser uma isenção.
Olumide 31/05

1
não é um DV, mas: A questão toda é sobre a declaração / escopo das variáveis ​​nomeadas. Um temporário ("objeto anônimo" não é um termo) não é uma variável nomeada, nem é uma declaração, nem está sujeita ao escopo (a menos que esteja vinculado a uma constreferência com escopo próprio). É uma expressão que vive e morre dentro de sua declaração (onde quer que esteja). Portanto, é totalmente irrelevante.
underscore_d

Foo();não é uma declaração; a questão é sobre declarações.
6777 MM
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.