$ g++ -o main *.cpp -O2 -std=c++11
$ ./main
42
65
84
$ nm ./main | grep var
$
J'ai pris l'exemple pour qu'il soit rapide et simple, mais rien n'empêche de calculer cette valeur à partir de deux entiers signés :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>staticunsignedconstvar{-12*(-14)};//#define var -12*(-14)voidfoo(unsigned){std::cout<<"unsigned"<<std::endl;}voidfoo(int){std::cout<<"int"<<std::endl;}intmain(){foo(var);}
Si les deux entiers sont négatifs et tiennent dans un int, le compilateur ne râlera pas (même avec tous les warnings)* pour l'expression const. Et on aura bien le résultat voulu pour foo. En revanche, si on veut le bon comportement pour le define, il faut se trimballer un cast supplémentaire : autant écrire directement une variable const.
Par ailleurs, l'indication que tu donnes (utiliser la bonne littérale), ne fonctionne comme qui dirait que pour les types à littérales, si on veut le faire avec ses propres définitions de type, c'est mort.
PS :
* en revanche, si ça ne rentre pas, il nous enverra bouler, même en mode par défaut :
Mon argument reste le même : mauvais choix de type. Si l'on veut une expression non signée, il est nécessaire qu'au moins un des deux opérandes soit non signé.
En effet, mais bon, je ne perçois pas vraiment cela comme un inconvénient (le type doit aussi être indiquer pour la déclaration de la variable constante).
Alors effectivement en O0, ça n'optimise pas (c'est le concept), j'en dirais pas autant de 02 :
1
2
3
$ g++ -o main main.cpp autre.cpp -O2 -std=c++11
$ nm main | grep var
$
(Evidemment, j'ai pris le même code que celui que tu indiques …)
Mais encore une fois, ici ce n'est pas une variable de configuration globale : elle est liée au scope. Ce qui fait qu'on aurait exactement le même comportement avec un define. Evidemment, deux variables de même nom dans des scopes différents ne sont pas les mêmes variables et peuvent avoir des valeurs différentes (rien de neuf sous le soleil). Mais si tu passes ne serait-ce que l'une des deux dans le scope global (ajout de extern ici), le compilateur t'enverra gentiment bouler.
J'ai pris l'exemple pour qu'il soit rapide et simple, mais rien n'empêche de calculer cette valeur à partir de deux entiers signés :
1
2
staticunsignedconstvar{-12*(-14)};//#define var -12*(-14)
Mon argument reste le même : mauvais choix de type. Si l'on veut une expression non signée, il est nécessaire qu'au moins un des deux opérandes soit non signé.
En vertu de quoi ? Si je suis capable de donner la garantie que la valeur que je donne à une variable est bien dans le range voulu, c'est définit et sémantiquement complètement valide, en particulier si je veux être sûr par la suite que l'élément en question est bien positif (typiquement, si c'est une taille).
1
2
3
4
5
6
7
8
9
10
intconste1{-42};intconste2{-12};static_assert(0<=e1*e2&&e1*e2<=std::numeric_limits<int>::max(),"cet assert est inutile, le compilateur le vérifie déjà");unsignedconstvar{e1*e2};intmain(){returnvar;}
L'inconvénient n'est pas dans le nommage explicite mais dans la limitation aux types primitifs, si on veut une variable const de type complexe, on n'a pas de littérale pour ça :
Outre le fait qu'avec un define, ce serait casse gueule (ndlr : pas ici, en revanche pour les tableaux …), encore une fois le compilateur sera optimisant :
1
2
3
4
5
6
$ g++ -o main main.cpp -O0 -std=c++11
$ nm ./main | grep cplx
0000000000400da0 r _ZL4cplx
$ g++ -o main main.cpp -O2 -std=c++11
$ nm ./main | grep cplx
$
Pour le reste, en C++, c'est 0 avantage. Inversement, l'utilisation de variables const définies dans le bon scope apporte tout ce dont on a besoin pour avoir le même niveau d'optimisation que les define avec un typage plus fort et surtout valable pour tous les types y compris les non-primitifs.
Mais encore une fois, ici ce n'est pas une variable de configuration globale : elle est liée au scope. Ce qui fait qu'on aurait exactement le même comportement avec un define. Evidemment, deux variables de même nom dans des scopes différents ne sont pas les mêmes variables et peuvent avoir des valeurs différentes (rien de neuf sous le soleil). Mais si tu passes ne serait-ce que l'une des deux dans le scope global (ajout de extern ici), le compilateur t'enverra gentiment bouler.
Au temps pour moi, je ne sais pas pourquoi j'ai continué mon raisonnement avec l'idée de variable partagée… Donc oui, cette technique donne le même résultat qu'une macroconstante. J'avoue cependant qu'en tant que programmeur C, je ne peux m'empêcher de trouver l'inculsion de définitions via un en-tête assez crade. Mais bon, cela reste deux langages différents.
Mon argument portait sur la macroconstante, par sur la variable. La définition correcte devrait être celle ci-dessous dans le cas où l'on souhaite obtenir une expression non-signée.
1
#define var ((-12)*(-14)*1U)
L'inconvénient n'est pas dans le nommage explicite mais dans la limitation aux types primitifs, si on veut une variable const de type complexe, on n'a pas de littérale pour ça :
Connectez-vous pour pouvoir poster un message.
Connexion
Pas encore membre ?
Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte