C ++ 0x tornará o código a seguir e um código semelhante malformado, porque requer a chamada conversão de estreitamento de a double
em a int
.
int a[] = { 1.0 };
Estou me perguntando se esse tipo de inicialização é muito usado em código do mundo real. Quantos códigos serão quebrados por essa mudança? É muito difícil consertar isso em seu código, se ele for afetado de alguma forma?
Para referência, consulte 8.5.4 / 6 de n3225
Uma conversão de estreitamento é uma conversão implícita
- de um tipo de ponto flutuante para um tipo inteiro, ou
- de long double para double ou float, ou de double para float, exceto onde a fonte é uma expressão constante e o valor real após a conversão está dentro do intervalo de valores que podem ser representados (mesmo que não possa ser representado exatamente), ou
- de um tipo de número inteiro ou tipo de enumeração sem escopo para um tipo de ponto flutuante, exceto onde a fonte é uma expressão constante e o valor real após a conversão se ajustará ao tipo de destino e produzirá o valor original quando convertido de volta ao tipo original, ou
- de um tipo inteiro ou tipo de enumeração sem escopo para um tipo inteiro que não pode representar todos os valores do tipo original, exceto onde a fonte é uma expressão constante e o valor real após a conversão se ajustará ao tipo de destino e produzirá o valor original quando convertido de volta ao tipo original.
c++
c++11
survey
aggregate-initialization
Johannes Schaub - litb
fonte
fonte
0
já é um deint
qualquer maneira.){
inicializadores de chaves}
e o único uso legado deles é para matrizes e estruturas POD. Além disso, se o código existente tiver conversões explícitas a que pertencem, ele não quebrará.int a = 1.0;
ainda é válido.Respostas:
Corri para essa alteração significativa quando usei o GCC. O compilador imprimiu um erro para um código como este:
void foo(const unsigned long long &i) { unsigned int a[2] = {i & 0xFFFFFFFF, i >> 32}; }
Felizmente, as mensagens de erro eram diretas e a correção era simples:
void foo(const unsigned long long &i) { unsigned int a[2] = {static_cast<unsigned int>(i & 0xFFFFFFFF), static_cast<unsigned int>(i >> 32)}; }
O código estava em uma biblioteca externa, com apenas duas ocorrências em um arquivo. Não acho que a alteração significativa afetará muito o código. Os novatos podem ficar confusos, no entanto.
fonte
Eu ficaria surpreso e desapontado comigo mesmo se soubesse que qualquer código C ++ que escrevi nos últimos 12 anos teve esse tipo de problema. Mas a maioria dos compiladores teria lançado avisos sobre qualquer "estreitamento" de tempo de compilação o tempo todo, a menos que esteja faltando alguma coisa.
Essas conversões também estão reduzindo?
unsigned short b[] = { -1, INT_MAX };
Nesse caso, acho que eles podem surgir com um pouco mais de frequência do que o exemplo do tipo flutuante para o tipo integral.
fonte
Um exemplo prático que encontrei:
float x = 4.2; // an input argument float a[2] = {x-0.5, x+0.5};
O literal numérico é implicitamente o
double
que causa a promoção.fonte
float
escrevendo0.5f
. ;)float
for um typedef ou parâmetro de modelo (pelo menos sem perda de precisão), mas o ponto é que o código como escrito funcionou com a semântica correta e se tornou um erro com C ++ 11. Ou seja, a definição de uma "mudança significativa".Eu não ficaria tão surpreso se alguém fosse pego por algo como:
float ra[] = {0, CHAR_MAX, SHORT_MAX, INT_MAX, LONG_MAX};
(na minha implementação, os dois últimos não produzem o mesmo resultado quando convertidos de volta para int / long, portanto, estão se estreitando)
Não me lembro de ter escrito isso, no entanto. Só é útil se uma aproximação dos limites for útil para algo.
Isso também parece pelo menos vagamente plausível:
void some_function(int val1, int val2) { float asfloat[] = {val1, val2}; // not in C++0x double asdouble[] = {val1, val2}; // not in C++0x int asint[] = {val1, val2}; // OK // now do something with the arrays }
mas não é totalmente convincente, porque se eu sei que tenho exatamente dois valores, por que colocá-los em matrizes em vez de apenas
float floatval1 = val1, floatval1 = val2;
? Qual é a motivação, porém, por que isso deveria compilar (e funcionar, desde que a perda de precisão esteja dentro da precisão aceitável para o programa), enquantofloat asfloat[] = {val1, val2};
não deveria? De qualquer forma, estou inicializando dois flutuantes de dois ints, mas em um caso os dois flutuantes são membros de um agregado.Isso parece particularmente difícil nos casos em que uma expressão não constante resulta em uma conversão de estreitamento, embora (em uma implementação particular), todos os valores do tipo de origem sejam representáveis no tipo de destino e conversíveis de volta aos seus valores originais:
char i = something(); static_assert(CHAR_BIT == 8); double ra[] = {i}; // how is this worse than using a constant value?
Supondo que não haja bug, presumivelmente a correção é sempre tornar a conversão explícita. A menos que você esteja fazendo algo estranho com macros, acho que um inicializador de array só aparece próximo ao tipo do array, ou pelo menos a algo que representa o tipo, que pode ser dependente de um parâmetro de template. Portanto, um elenco deve ser fácil, embora detalhado.
fonte
Tente adicionar -Wno-estreitamento às suas CFLAGS, por exemplo:
CFLAGS += -std=c++0x -Wno-narrowing
fonte
Erros de conversão restritos interagem mal com as regras de promoção de inteiros implícitos.
Eu tive um erro com o código que parecia
struct char_t { char a; } void function(char c, char d) { char_t a = { c+d }; }
O que produz um erro de conversão de estreitamento (que está correto de acordo com o padrão). O motivo é que
c
ed
implicitamente são promovidos aint
e o resultadoint
não pode ser reduzido a char em uma lista de inicializadores.OTOH
void function(char c, char d) { char a = c+d; }
é claro que ainda está bem (caso contrário, o inferno iria explodir). Mas, surpreendentemente, mesmo
template<char c, char d> void function() { char_t a = { c+d }; }
está ok e compila sem aviso se a soma de ced for menor que CHAR_MAX. Ainda acho que isso é um defeito no C ++ 11, mas as pessoas pensam o contrário - possivelmente porque não é fácil de consertar sem se livrar de qualquer conversão implícita de inteiro (que é uma relíquia do passado, quando as pessoas escreveram código como
char a=b*c/d
e esperava que funcionasse mesmo se (b * c)> CHAR_MAX) ou reduzindo erros de conversão (que são possivelmente uma coisa boa).fonte
unsigned char x; static unsigned char const m = 0x7f; ... unsigned char r = { x & m };
<- estreitando a conversão dentro de {}. Mesmo? Portanto, o operador e também converte implicitamente caracteres não assinados em int? Bem, eu não me importo, o resultado ainda é garantido ser um char não assinado, argh.Foi realmente uma mudança significativa, pois a experiência da vida real com esse recurso mostrou que o gcc transformou o estreitamento em um aviso de um erro em muitos casos devido a problemas da vida real com a portabilidade de bases de código C ++ 03 para C ++ 11. Veja este comentário em um relatório de bug do gcc :
fonte
Parece que o GCC-4.7 não fornece mais erros para estreitar as conversões, mas sim avisos.
fonte