Et non, les compilos C et C++ ne sont pas les même. O_o
$ ls -al clang++
lrwxrwxrwx 1 luc luc 5 févr. 22 04:29 clang++ -> clang*
Même binaire, juste un mode par défaut qui change des petites règles par ci par là. Ils sont "suffisants" les mêmes pour qu’ils sachent exploiter const
différemment le jour hypothétique où la norme C déciderait de changer. AMA. Mais je peux me tromper.
Parce que les erreurs causées "par" le préprocesseur sont abominables?
Mais en C on a que ça ! Il faut tout changer alors. Et puis surtout, on a connu plus dangereux que des defines. Pour les macros, je veux bien. Mais les defines …
En C, vous n’avez que ça je vois en effet. J’avais raté les implications de la sémantique particulière de const
en C, et croyais que cela donnerait les mêmes garanties qu’en C++.
Je veux bien que tu argumentes plus sur ces erreurs.
https://gcc.godbolt.org/z/Yfcv6hM5M Et encore, c’est une version simple où l’erreur n’est pas enterrées sous 12 couches d’intermédiaires.
#if 0
#define TOTO 42
#else
#define TOTO "toto"
#endif
int g(int);
int f(int i) {
return g(TOTO);
}
Bref, ça compile avec juste un
<source>: In function 'f':
<source>:4:14: warning: passing argument 1 of 'g' makes integer from pointer without a cast [-Wint-conversion]
4 | #define TOTO "toto"
| ^~~~~~
| |
| char *
<source>:10:14: note: in expansion of macro 'TOTO'
10 | return g(TOTO);
| ^~~~
<source>:7:7: note: expected 'int' but argument is of type 'char *'
7 | int g(int);
| ^~~
On est chanceux aujourd’hui, gcc & clang savent suivre les macros. Mais avec un gcc 4.8.5 typique des RedHat 7 que l’on se coltine toujours, c’est
<source>: In function 'f':
<source>:10:5: warning: passing argument 1 of 'g' makes integer from pointer without a cast [enabled by default]
return g(TOTO);
^
<source>:7:5: note: expected 'int' but argument is of type 'char *'
int g(int);
^
La même en C++ avec du constexpr auto
, cela devient une erreur:
<source>: In function 'int f(int)':
<source>:10:14: error: invalid conversion from 'const char*' to 'int' [-fpermissive]
10 | return g(TOTO);
| ^~~~
| |
| const char*
<source>:7:7: note: initializing argument 1 of 'int g(int)'
7 | int g(int);
| ^~~
Cela devient encore plus sympa quand des macros sont utilisées sans CRIER
#define direction 1
......
if (toto.direction == 42)
Qui irait faire ça, hein?
ce n’est pas comme si un célèbre compilateur venait avec une lib qui définissait min()
comme une macro…
Ouais … N’empèche que ça intéresse les gens. Et que […] Et bien ça donnera 5 et puis c’est tout.
Jusqu’au jour où les compilos décideront de traiter l’UB différemment… (je vous fais confiance si vous dites que cette utilisation particulière de const
+ volatile
est UB en C). Autant il y a des endroits où cela ne devrait pas être très grave (genre supposer que x+1 > x
est toujours vrai (avec des signés)), autant il y en a où je ne me risquerai pas.