error: ISO C++ forbids compound-literals [RESOLVIDO]

1. error: ISO C++ forbids compound-literals [RESOLVIDO]

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 30/11/2021 - 21:48h

Boa Noite a todos!

Dessa vez não montei um exemplo pra perguntar, mas se for necessário faço isso.
Eu desenvolvi um código em C que lê um arquivo para uma Struct. Ele funciona!
Agora estou tentando fazer esse mesmo código funcionar em C++, e sendo C++ mais exigente, estou tendo dificuldades de encontrar a solução.

Tenho um for que lê o arquivo:
for(int Character, US = -1; (Character = getc(MyFile)) != EOF;) { 

Tenho um strcat bugado que adiciona um caracter de cada vez para a variável Registered, igual a std::string Registered += Character; mas no caso abaixo ele é um vetor de char.
char Registered[20] = {0};
strcat(Registered, (char[2]) {Character, '\0'});

Abaixo o erro do Debug que não estou sabendo como consertar:
database.cpp:56:64: error: ISO C++ forbids compound-literals [-Wpedantic]
56 | strcat(Registered, (char[2]) {Character, '\0'});
| ^
database.cpp:56:49: error: narrowing conversion of ‘Character’ from ‘int’ to ‘char’ [-Wnarrowing]
56 | strcat(Registered, (char[2]) {Character, '\0'});
| ^~~~~~~~~
database.cpp:56:48: error: taking address of temporary array
56 | strcat(Registered, (char[2]) {Character, '\0'});
| ^~~~~~~~~~~~~~~~~

Posso compreender que usar std::string é mais simples, mas meu intuito é saber consertar o problema porque neste momento eu quero rodar código C no C++, me dando a opção de compilar em C ou C++, por isso eu gostaria de consertar o strcat no C++ de forma que esse conserto rode também em C. Meu motivo pra isso é apenas aprendizado, baseado em testes, saber o que posso fazer e entender isso.

Porque não entendi?
1) Não entendi quando ele se refere a compound-literals
2) Não entendi narrowing que é o motivo dele não aceitar a conversão
3) Creio que o 3o Erro tenha a ver com o 2o Erro



  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 01/12/2021 - 07:02h

ApprenticeX escreveu:

Boa Noite a todos!

Dessa vez não montei um exemplo pra perguntar, mas se for necessário faço isso.
Eu desenvolvi um código em C que lê um arquivo para uma Struct. Ele funciona!
Agora estou tentando fazer esse mesmo código funcionar em C++, e sendo C++ mais exigente, estou tendo dificuldades de encontrar a solução.

Tenho um for que lê o arquivo:
for(int Character, US = -1; (Character = getc(MyFile)) != EOF;) { 

Tenho um strcat bugado que adiciona um caracter de cada vez para a variável Registered, igual a std::string Registered += Character; mas no caso abaixo ele é um vetor de char.
char Registered[20] = {0};
strcat(Registered, (char[2]) {Character, '\0'});

Abaixo o erro do Debug que não estou sabendo como consertar:
database.cpp:56:64: error: ISO C++ forbids compound-literals [-Wpedantic]
56 | strcat(Registered, (char[2]) {Character, '\0'});
| ^


Essa construção de fazer conversão de tipo para array é válida em C, mas não em C++. Veja o que acontece quando eu tento compilar o mesmo código em C e C++.
// x.c: código válido em C, mas inválido em C++.
#include <stdio.h>
#include <string.h>

int main(void){
char buf[100]="Paul";
strcat(buf, (char[2]){'o', '\0'});
puts(buf);
}
$ gcc -std=c11 -Wall -Werror -O2 -pedantic-errors -c x.c   # compila sem erros

$ g++ -std=c++17 -Wall -Werror -O2 -pedantic-errors -c x.c
x.c: In function ‘int main()’:
x.c:6:33: error: ISO C++ forbids compound-literals [-Wpedantic]
strcat(buf, (char[2]){'o', '\0'});
^
x.c:6:23: error: taking address of temporary array
strcat(buf, (char[2]){'o', '\0'});
^~~~~~~~~~~


Não bastasse isso, a própria conversão usando nome do tipo entre parênteses é obsoleto em C++.

Esse é um motivo pelo qual, quando você perguntou em outro tópico como usar strcat() para colocar apenas um caráter no final da string, eu tive vontade de apontar que a resposta que indicou essa solução não era lá muito boa, mas como você, naquele tópico, perguntou especificamente sobre C, não sobre C++, e deu a entender que queria usar strcat(), então a resposta era válida — possivelmente sub-ótima, porém válida em C.

Você não postou o código completo naquela postagem nem aqui, mas eu imagino que você queria algo como o seguinte.
char Reg[20]="";
for(int Ch; (Ch = getc(MyFile)) != EOF;) { // Perigo aqui, pois você deveria limitar a, no máximo, 19 iterações.
/* Acrescenta o caráter Ch ao final da string. */
strcat(Reg, (char[2]){Ch, '\0'}); // Tentativa de fazer, válida em C, mas problemática de mais de uma maneira.
/* ... usa Reg para outras coisas, se for o caso... */
}

“Mais de uma maneira” porque, além de não ser portável, usar strcat() dentro de um laço de repetição transforma seu algoritmo em quadrático, porque a própria strcat() sempre tem de varrer o conteúdo do array de destino inteiro para encontrar o final da string. Você definitivamente não quer isso.

Considere deixar de usar strcat(), e fazer de um modo que não tenha de varrer o array a caa iteração do laço de repetição, com algo parecido com o seguinte (e que funciona em C++, e que não corre o risco de extrapolar o tamanho do array).
char Reg[20]="";
for(int Ch, index=0; (Ch = getc(MyFile)) != EOF && index<sizeof Reg-1;) {
Reg[index++]=Ch;
Reg[index]='\0';
/* ... usa Reg para outras coisas, se for o caso... */
}


database.cpp:56:49: error: narrowing conversion of ‘Character’ from ‘int’ to ‘char’ [-Wnarrowing]
56 | strcat(Registered, (char[2]) {Character, '\0'});
| ^~~~~~~~~
database.cpp:56:48: error: taking address of temporary array
56 | strcat(Registered, (char[2]) {Character, '\0'});
| ^~~~~~~~~~~~~~~~~


A primeira mensagem é bem óbvia, creio eu: você está tentando fazer um inteiro, que tipicamente requer dois ou quatro bytes, caber num espaço de apenas um byte. Já o segundo significa que você está tentando obter um endereço, pelo decaimento de array para ponteiro, de uma expressão temporária, algo que não é valido em C++ (e, a bem da verdade, nem em C, num caso geral, mas o sentido da construção entre chaves, que é aceito em C, é diferente daquele que teria no C++).

Posso compreender que usar std::string é mais simples, mas meu intuito é saber consertar o problema porque neste momento eu quero rodar código C no C++, me dando a opção de compilar em C ou C++, por isso eu gostaria de consertar o strcat no C++ de forma que esse conserto rode também em C. Meu motivo pra isso é apenas aprendizado, baseado em testes, saber o que posso fazer e entender isso.


Como eu disse acima, o melhor, em termos de desempenho, seria não usar strcat(). Aliás, não só por desempenho, mas também porque strcat() tem a falha de não verificar se o destino da concatenação tem espaço suficiente para acomodar todos os caracteres que serão concatenados; seria melhor usar strncat() ou, se o seu sistema suportar, strlcat() (no Linux, ela costuma residir na libbsd, em vez de na libc).

Contudo, se você realmente quiser seguir nessa linha de desnecessária ineficiência, pode fazer algo como o seguinte, que também funciona tanto em C quanto em C++ (e que é, no fim das contas, algo muito parecido com o que faz efetivamente a construção em C que o C++ não aceita).
char Reg[20]="";
for(int Ch; (Ch = getc(MyFile)) != EOF;) { // Perigo aqui, pois você deveria limitar a, no máximo, 19 iterações.
char ch_str[2]={(char)Ch, 0};
strcat(Reg, ch_str); // Tentativa de fazer, válida em C, mas problemática de mais de uma maneira.
/* ... usa Reg para outras coisas, se for o caso... */
}



... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)

3. Re: error: ISO C++ forbids compound-literals [RESOLVIDO]

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 30/11/2021 - 22:08h

database.cpp:56:64: error: ISO C++ forbids compound-literals [-Wpedantic]
56 | strcat(Registered, (char[2]) {Character, '\0'});
| ^
database.cpp:56:49: error: narrowing conversion of ‘Character’ from ‘int’ to ‘char’ [-Wnarrowing]

Se atente ao '^' que é onde o compilador achou o erro.
No caso, observe que, você colocou ')' para fechar o (char[2]){, ou seja tá como se fosse assim: (char[2]){) errado, fechando chaves { com parênteses )




4. Re: error: ISO C++ forbids compound-literals [RESOLVIDO]

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 30/11/2021 - 22:58h


SamL escreveu:

database.cpp:56:64: error: ISO C++ forbids compound-literals [-Wpedantic]
56 | strcat(Registered, (char[2]) {Character, '\0'});
| ^
database.cpp:56:49: error: narrowing conversion of ‘Character’ from ‘int’ to ‘char’ [-Wnarrowing]

Se atente ao '^' que é onde o compilador achou o erro.
No caso, observe que, você colocou ')' para fechar o (char[2]){, ou seja tá como se fosse assim: (char[2]){) errado, fechando chaves { com parênteses )


Oi Sam, obrigado por responder, mas neste caso, Os parenteses () Estão fechando a strcat(), as chaves {} estão fechando Character, e os parentes dentro do strcat fecham char[2]

Esse código o compilador C aceita normalmente, teria outra forma de fechar isso em C++, ou seja, eu teria que tirar as Chaves? {}



5. Re: error: ISO C++ forbids compound-literals [RESOLVIDO]

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 01/12/2021 - 15:02h

paulo1205 escreveu:
Esse é um motivo pelo qual, quando você perguntou em outro tópico como usar strcat() para colocar apenas um caráter no final da string, eu tive vontade de apontar que a resposta que indicou essa solução não era lá muito boa, mas como você, naquele tópico, perguntou especificamente sobre C, não sobre C++, e deu a entender que queria usar strcat(), então a resposta era válida — possivelmente sub-ótima, porém válida em C.

Sim na pergunta anterior o objetivo era esse, pois salvei aquele exemplo. A questão de migrar ele para C++ foi uma idéia posterior, para estudar o mesmo comportamento e entender.

“Mais de uma maneira” porque, além de não ser portável, usar strcat() dentro de um laço de repetição transforma seu algoritmo em quadrático, porque a própria strcat() sempre tem de varrer o conteúdo do array de destino inteiro para encontrar o final da string. Você definitivamente não quer isso.

Bom saber isso! Eu venho começando a entender a necessidade de saber o que cada função faz pra saber como ela trabalha, por isso esse enfase na strcat e uma pergunta sobre ela que fiz pra conhecer melhor a função. Essa sua resposta reforça ainda mais a necessidade de conhecer bem as funções que vamos usar!

Considere deixar de usar strcat(), e fazer de um modo que não tenha de varrer o array a caa iteração do laço de repetição, com algo parecido com o seguinte (e que funciona em C++, e que não corre o risco de extrapolar o tamanho do array).
char Reg[20]="";
for(int Ch, index=0; (Ch = getc(MyFile)) != EOF && index<sizeof Reg-1;) {
Reg[index++]=Ch;
Reg[index]='\0';
/* ... usa Reg para outras coisas, se for o caso... */
}

Curiosamente eu já fiz assim em algum momento, esse mesmo código, mas não encontrei onde anotei, então estou montando outro exemplo pra guardar usando o index como vc sugere. No meu código não é necessário controlar o tamanho do array pq o valor que o campo recebe é exato, e se passar é porque o arquivo foi corrompido então ele nem chegaria a executar o laço. Mas gostei da explicação porque reforça minha atenção para o uso indevido ou inapropriado de funções.

A primeira mensagem é bem óbvia, creio eu: você está tentando fazer um inteiro, que tipicamente requer dois ou quatro bytes, caber num espaço de apenas um byte.

Não me lembrei do tamanho no caso acima, ainda não sei como anotar isso, mas descobrirei uma forma de anotar para consultar, porque esse tipo de observação é como uma falta de atenção, mas no meu caso não é falta de atenção, e sim minha mente que não pensou no tamanho das variáveis. Por isso não entendi direito a msg do debug, se ao menos ele tivesse falado em tamanho, eu talvez tivesse raciocinado melhor.

Já o segundo significa que você está tentando obter um endereço, pelo decaimento de array para ponteiro, de uma expressão temporária, algo que não é valido em C++ (e, a bem da verdade, nem em C, num caso geral, mas o sentido da construção entre chaves, que é aceito em C, é diferente daquele que teria no C++).

Isso eu ainda estou encontrando uma forma de compreender melhor, ponteiros, decaimento, chegarei lá

Contudo, se você realmente quiser seguir nessa linha de desnecessária ineficiência, pode fazer algo como o seguinte, que também funciona tanto em C quanto em C++ (e que é, no fim das contas, algo muito parecido com o que faz efetivamente a construção em C que o C++ não aceita).
char Reg[20]="";
for(int Ch; (Ch = getc(MyFile)) != EOF;) { // Perigo aqui, pois você deveria limitar a, no máximo, 19 iterações.
char ch_str[2]={(char)Ch, 0};
strcat(Reg, ch_str); // Tentativa de fazer, válida em C, mas problemática de mais de uma maneira.
/* ... usa Reg para outras coisas, se for o caso... */
}

Obrigado por esse exemplo, meu objetivo é aprender, o que aprendo neste exemplo que considero importante e que guardo é a conversão, o entendimento dela, notei que ao invés de usar \0 vc usou apenas 0, não sabia que podia fazer dessa forma, isso me serve como exemplo de converter int para um peq vetor de char.

Também considerei importante vc citar a questão do desempenho, porque sempre vou levar isso em conta! Embora minha pergunta tenha a ver com o entendimento do funcionamento do que postei, saber sobre o desempenho me alerta para códigos futuros que eu precise mesmo implementar. Pois tenho que me manter atento ao funcionamento das funções, para saber se posso fazer algo mais eficiente e não repetitivo.

O Código em si do que postei, o que ele faz é varrer um arquivo marcado separando os campos para uma Struct, cada vez que ele executa o for ele salva 1 registro guardando seus campos na struct, zera as variáveis e repete o laço for para o próximo registro. No momento esse programa não tem utilidade a não ser para guardar de exemplo as inúmeras formas que posso fazer a mesma coisa, assim saberei qual a forma melhor que usarei quando precisar. Tanto como C, como em C++.








Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts