Estou tentando encontrar uma maneira conveniente de inicializar estruturas C ++ 'pod'. Agora, considere a seguinte estrutura:
struct FooBar {
int foo;
float bar;
};
// just to make all examples work in C and C++:
typedef struct FooBar FooBar;
Se eu quiser inicializar isso convenientemente em C (!), Eu poderia simplesmente escrever:
/* A */ FooBar fb = { .foo = 12, .bar = 3.4 }; // illegal C++, legal C
Observe que quero evitar explicitamente a seguinte notação, porque me parece que foi feita para quebrar meu pescoço se eu mudar alguma coisa na estrutura no futuro:
/* B */ FooBar fb = { 12, 3.4 }; // legal C++, legal C, bad style?
Para obter o mesmo (ou pelo menos semelhante) em C ++ como no /* A */
exemplo, eu teria que implementar um construtor idiota:
FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar) {}
// ->
/* C */ FooBar fb(12, 3.4);
O que é bom para ferver água, mas não é adequado para pessoas preguiçosas (a preguiça é uma coisa boa, certo?). Além disso, é tão ruim quanto o /* B */
exemplo, pois não indica explicitamente qual valor vai para qual membro.
Então, minha pergunta é basicamente como eu posso conseguir algo semelhante a /* A */
ou melhor em C ++? Como alternativa, eu ficaria bem com uma explicação sobre por que não gostaria de fazer isso (ou seja, por que meu paradigma mental é ruim).
EDITAR
Por conveniente , quero dizer também de manutenção e não redundante .
fonte
Respostas:
Inicializações designadas serão suportadas no c ++ 2a, mas você não precisa esperar, porque elas são suportadas oficialmente pelo GCC, Clang e MSVC.
GCC Demo MSVC Demo
fonte
Como
style A
não é permitido em C ++ e você não desejastyle B
, que tal usarstyle BX
:Pelo menos ajuda até certo ponto.
fonte
foo
ebar
no futuro. C ainda inicializaria os campos que queremos, mas C ++ não. E este é o ponto da questão - como obter o mesmo resultado em C ++. Quero dizer, o Python faz isso com argumentos nomeados, C - com campos "nomeados" e C ++ também deve ter algo, espero.explicit FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar)
. Observe a palavra-chave explícita . Mesmo quebrar o padrão é melhor no que diz respeito à segurança. Em Clang: -Wno-c99-extensionsVocê poderia usar um lambda:
Mais informações sobre esse idioma podem ser encontradas no blog de Herb Sutter .
fonte
fb.XXX = YYY
.Extraia os conteúdos em funções que os descrevam (refatoração básica):
Eu sei que o estilo está muito próximo do que você não queria usar, mas permite a substituição mais fácil dos valores constantes e também os explica (portanto, não é necessário editar comentários), se eles mudarem.
Outra coisa que você pode fazer (já que você é preguiçoso) é tornar o construtor incorporado, para que você não precise digitar tanto (removendo "Foobar ::" e o tempo gasto alternando entre os arquivos he cpp):
fonte
Sua pergunta é um pouco difícil, porque até a função:
pode ser chamado como:
devido às regras de promoção e conversões para tipos numéricos incorporados. (C nunca foi realmente fortemente digitado)
No C ++, o que você deseja é possível, embora com a ajuda de modelos e asserções estáticas:
Em C, você pode nomear os parâmetros, mas nunca irá além.
Por outro lado, se tudo o que você deseja é nomeado parâmetros, você escreve muito código complicado:
E você pode adicionar proteção de promoção de tipo, se quiser.
fonte
As interfaces C ++ de muitos compiladores (incluindo GCC e clang) entendem a sintaxe do inicializador C. Se você puder, basta usar esse método.
fonte
private: FooBar(float x, int y) {};
Ainda outra maneira em C ++ é
fonte
inline
!Opção D:
FooBar FooBarMake(int foo, float bar)
C legal, C ++ legal. Facilmente otimizável para PODs. Claro que não há argumentos nomeados, mas isso é como todo o C ++. Se você deseja argumentos nomeados, o Objetivo C deve ser a melhor escolha.
Opção E:
C legal, C ++ legal. Argumentos nomeados.
fonte
FooBar fb = {};
no C ++, ele inicializa por padrão todos os membros da estrutura.Eu sei que essa pergunta é antiga, mas há uma maneira de resolver isso até que o C ++ 20 finalmente traga esse recurso de C para C ++. O que você pode fazer para resolver isso é usar macros de pré-processador com static_asserts para verificar se a inicialização é válida. (Eu sei que as macros geralmente são ruins, mas aqui não vejo outra maneira.) Veja o código de exemplo abaixo:
Então, quando você tiver uma estrutura com atributos const, poderá fazer o seguinte:
É um pouco inconveniente, porque você precisa de macros para todos os números possíveis de atributos e precisa repetir o tipo e o nome da sua instância na chamada de macro. Além disso, você não pode usar a macro em uma declaração de retorno, porque as declarações vêm após a inicialização.
Mas isso resolve o seu problema: quando você altera a estrutura, a chamada falha no momento da compilação.
Se você usa C ++ 17, pode até tornar essas macros mais rigorosas forçando os mesmos tipos, por exemplo:
fonte
O caminho
/* B */
é bom em C ++ e também o C ++ 0x estenderá a sintaxe, sendo útil também para contêineres em C ++. Eu não entendo por que você chama isso de estilo ruim?Se você deseja indicar parâmetros com nomes, pode usar a biblioteca de parâmetros boost , mas isso pode confundir alguém não familiarizado com ela.
Reordenar membros de estrutura é como reordenar parâmetros de funções, essa refatoração pode causar problemas se você não fizer isso com muito cuidado.
fonte
E essa sintaxe?
Acabei de testar em um Microsoft Visual C ++ 2015 e em g ++ 6.0.2. Trabalhando bem.
Você também pode criar uma macro específica se desejar evitar a duplicação do nome da variável.
fonte
clang++
3.5.0-10 com-Weverything -std=c++1z
parece confirmar isso. Mas não parece certo. Você sabe onde o padrão confirma que este é C ++ válido?ABCD abc = { abc.b = 7, abc.a = 5 };
.Para mim, a maneira mais preguiçosa de permitir a inicialização em linha é usar essa macro.
Essa macro cria atributo e método de auto-referência.
fonte