Por que C ++ não permite estruturas anônimas?

92

Alguns compiladores C ++ permitem uniões e estruturas anônimas como uma extensão do C ++ padrão. É um pouco de açúcar sintático que às vezes é muito útil.

Qual é a lógica que impede que isso faça parte do padrão? Existe um obstáculo técnico? Filosófico? Ou apenas não o suficiente para justificar isso?

Aqui está um exemplo do que estou falando:

struct vector3 {
  union {
    struct {
      float x;
      float y;
      float z;
    };
    float v[3];
  };
};

Meu compilador aceitará isso, mas avisa que "struct / union sem nome" é uma extensão não padrão para C ++ .

Adrian McCarthy
fonte
3
Obviamente, há alguma confusão sobre o que você quer dizer. Você poderia fornecer um exemplo de código que só compila devido a uma extensão do compilador?
Rob Kennedy
72
Observe que há dois conceitos que parecem semelhantes, mas são muito diferentes: estruturas sem nome e estruturas anônimas . O primeiro é este, que C ++ suporta: struct { int i; } a; a.i = 0;(o tipo não tem nome). O segundo é este, que C ++ não suporta: struct { int i; }; i = 0;(o tipo não tem nome e escapa para o escopo circundante). C ++, no entanto, faz suportar ambos sem nome e anônimos sindicatos .
Johannes Schaub - litb
Isso se parece com a biblioteca de vetores VMMLib bastante interessante. Acredito que o problema é que o sindicato contém uma estrutura sem nome, mas não tenho certeza.
greyfade
1
FWIW É "anônimo", não "sem nome", e os sindicatos são suportados como diz a litb. stackoverflow.com/q/14248044/560648
Lightness Races in Orbit
1
@AdrianMcCarthy: Tudo bem (FSVO "bom"; compilador incômodo sendo enigmático), mas precisamente "sem nome" é um conceito padrão não relacionado.
Lightness Races in Orbit

Respostas:

50

Como outros apontaram, uniões anônimas são permitidas no C ++ padrão, mas estruturas anônimas não.

A razão para isso é que C oferece suporte a uniões anônimas, mas não a estruturas anônimas *, portanto, C ++ oferece suporte ao primeiro para compatibilidade, mas não ao último porque não é necessário para compatibilidade.

Além disso, não há muito uso para estruturas anônimas em C ++. O uso que você demonstrar, ter uma estrutura que contém três carros alegóricos que podem ser referidos quer por .v[i], ou .x, .ye .z, eu acredito que resulta em um comportamento indefinido em C ++. C ++ não permite que você escreva para um membro de um sindicato, digamos .v[1], e depois leia de outro membro, digamos .y. Embora o código que faz isso não seja incomum, na verdade não está bem definido.

Os recursos do C ++ para tipos definidos pelo usuário fornecem soluções alternativas. Por exemplo:

struct vector3 {
  float v[3];
  float &operator[] (int i) { return v[i]; }
  float &x() { return v[0]; }
  float &y() { return v[1]; }
  float &z() { return v[2]; }
};

* C11 aparentemente adiciona estruturas anônimas, portanto, uma revisão futura do C ++ pode adicioná-las.

bames53
fonte
2
+1: Meu exemplo se baseia em comportamento indefinido em C ++ - algo que eu não sabia quando escrevi a pergunta.
Adrian McCarthy
2
"C ++ não permite que você escreva para um membro de um sindicato [...] e depois leia de outro membro" - a menos que esses membros sejam objetos de layout padrão e compartilhem uma sequência inicial comum de membros próprios, e você ' reescrever / ler seus membros dentro da referida sequência inicial comum. Isso é permitido (ou seja, definido).
sublinhado_d
5
@underscore_d: Sim, se os tipos forem de layout padrão com uma sequência inicial comum. No entanto, uma estrutura nunca pode criar um alias com uma matriz dessa maneira, porque as regras de "sequência inicial comum" do C ++ afirmam que uma sequência inicial comum só pode estar entre estruturas . Matrizes não são mencionadas, portanto, não podem ser apelidos como este.
Nicol Bolas
@NicolBolas Oh, haha ​​- acredite em mim - eu desejei muitas vezes que arrays e outros primitivos fossem incluídos nesta permissão! Mas não pensei muito sobre as possíveis limitações práticas disso, então provavelmente não é tão simples quanto parece atualmente. Meu comentário foi mais geral, mas pode ter corrido o risco de insinuar por omissão que eu pensei que matrizes foram incluídas nisso, então obrigado por adicioná-lo.
sublinhado_d
"A razão para isso é que C apóia uniões anônimas, mas não estruturas anônimas" - Não. Sua nota de rodapé esclarece que você estava falando sobre C99 ou antes aqui. O termo "união anônima" não aparece em nenhum lugar do padrão C99. O GCC afirma em um diagnóstico (com opções -std = c99 -pedantic) que "ISO C99 não oferece suporte a estruturas / uniões sem nome". O padrão não menciona nada sobre membros não nomeados, exceto campos de bits não nomeados. Não tenho certeza se as declarações de estrutura são declarações, mas se forem, as uniões anônimas são uma violação de restrição por 6.7p2, na melhor das hipóteses indefinidas.
21

Eu direi, você pode limpar sua vector3declaração usando apenas umunion

union vector3 {
  struct { float x, y, z; } ;
  float v[3] ;
} ;

Claro, estruturas anônimas eram uma extensão do MSVC . Mas o ISO C11 permite isso agora, e o gcc permite , assim como o compilador llvm da Apple.

Por que em C11 e não em C ++ 11? Não tenho certeza, mas praticamente falando, a maioria dos compiladores C ++ (gcc ++, MSVC ++ e C ++ da Apple) os suporta.

bobobobo
fonte
1
+1 para informações atualizadas. A razão de eu ter uma estrutura externa era porque o "código real" também tinha métodos.
Adrian McCarthy,
As únicas coisas que você não pode fazer com uma união são ter membros de dados estáticos ou usar herança .
bobobobo
2
Obrigado. Nunca soube que uma união poderia ser usada como uma estrutura ou classe.
Adrian McCarthy
Eu sei que o Sun Studio não suportava estruturas anônimas anteriores ao C ++ 11 por padrão. Se você estiver escrevendo código de plataforma cruzada e os compiladores não forem atualizados para C + 11, não use uma estrutura anônima.
íris
6

Não tenho certeza do que você quer dizer. Seção 9.5 da especificação C ++, cláusula 2:

Uma união da forma

union { member-specification } ;

é chamado de união anônima; ele define um objeto sem nome de tipo sem nome.

Você também pode fazer coisas assim:

void foo()
{
  typedef
  struct { // unnamed, is that what you mean by anonymous?
    int a;
    char b;
  } MyStructType; // this is more of a "C" style, but valid C++ nonetheless

  struct { // an anonymous struct, not even typedef'd
    double x;
    double y;
  } point = { 1.0, 3.4 };
}

Nem sempre muito útil ... embora às vezes útil em definições de macro desagradáveis.

Dan
fonte
11
-1 porque está dizendo que define uma estrutura anônima. Veja os comentários acima sobre a questão - você está definindo uma estrutura sem nome, não anônima.
Johannes Schaub - litb
1

Os sindicatos podem ser anônimos; ver a Norma, 9.5 parágrafo 2.

Que propósito você vê como uma estrutura ou classe anônima cumprindo? Antes de especular por que algo não está no padrão, gostaria de ter uma ideia de por que deveria estar, e não vejo um uso para uma estrutura anônima.

David Thornley
fonte
1

Com base na edição, nos comentários e neste artigo do MSDN: Anonymous Structures , arrisco um palpite - ele se encaixa mal com o conceito de encapsulamento. Eu não esperaria que um membro de uma classe mexesse com o namespace da minha classe além de simplesmente adicionar um membro. Além disso, as alterações na estrutura anônima podem afetar minha classe sem permissão.

JonM
fonte
1
Devido à forma como as estruturas / uniões anônimas são criadas (é uma sintaxe inline especial que não pode ser oculta, exceto por uma macro), você não pode se surpreender que algum membro que você está usando seja um membro anônimo. Portanto, não acho que esse raciocínio faça sentido. O motivo real é que as uniões anônimas são suportadas em C ++, apenas para compatibilidade com C. C não oferece suporte a estruturas anônimas (até C11) e, portanto, C ++ também não.
bames53
1

Seu código

union {
  struct {
    float x;
    float y;
    float z;
  };
  float v[3];
};

é como

union Foo {
   int;
   float v[3];
};

o que é certamente inválido (em C99 e antes).

A razão é provavelmente para simplificar a análise (em C), porque nesse caso você só precisa verificar se o corpo da estrutura / união tem apenas "declarações do declarador" como

Type field;

Dito isso, gcc e "outros compiladores" suportam campos sem nome como uma extensão.

Editar: estruturas anônimas agora são oficialmente suportadas em C11 (§6.7.2.1 / 13).

Kennytm
fonte
5
Do ponto de vista da análise, não acho que union { ... }seja diferente de struct { ... }. O primeiro é válido, mas o último não.
Johannes Schaub - litb
3
Dado o quão absurdamente difícil é analisar o C ++ em geral, duvido que o padrão comprometido não permitisse structs e uniões sem nome apenas para simplificar a análise.
Adrian McCarthy
@Adrian: Eu disse C, não C ++. C ++ adota a sintaxe de C e a estende. Provavelmente os criadores do C ++ não veem a necessidade de permitir membros de struct / union sem nome, então eles não mexem com essa parte da sintaxe.
kennytm
@Adrian, bom ponto Adrian, eu sempre não pensei que "muito difícil de implementar" seria uma preocupação de Bjarne e equipe
bobobobo
C e C ++ oferecem suporte a uniões sem nome, portanto, o comentário union { ... };inválido não está correto.
bames53