C'est surtout que, lorsque tu invoque une fonction sous la forme de
la valeur 1 est transformée en variable temporaire non nommée (temporaire car elle n'existe que pour l'appel de f et non nommée car tu ne dispose nulle part ailleurs d'un identifiant permettant de récupérer la valeur).Code:f<int>(1);
Or, la norme est particulièrement claire là dessus: lorsqu'il s'agit de travailler avec des références, seules les références constantes peuvent être initialisée avec des variables temporaires non nommées...
Comme tu ne respectes pas les règles, le compilateur s'en plaint violemment ;)
Les possibilités dont on dispose sont
et nous place face aux situations suivantes:Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 /* argument passé par valeur: il y a création d'une variable nommée t * qui est la copie de la variable passée en paramètre */ template< typename T> void f( T t) { } /* arguement passé par valeur constante: il y a création d'une variable * (rendue consttante pour l'occasion) nommée t * qui est la copie de la variable passée en paramètre */ template <typename T> void g(T const t) { } /* argument passé par référence constante: il n'y aura création * d'une variable temporaire non nommée que si le paramètre * passé n'est pas une variable du type adéquat à la base * t est un alias de la variable passée en paramètre (éventuellement * de la variable temporaire non nommée créée spécialement pour l'occasion) */ template <typename T> void h(T const & t) { } /* ne fonctionnera que lorsqu'on invoque la fonction en lui passant * une variable existante par ailleurs * t est un alias de la variable passée en paramètre */ template <typename T> void i(T & t) { }
Mais pour en revenir au choix de passer l'arguement sous la forme d'une référence constante ou non constante...Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 int main() { f<int> (2); // ok, création d'une variable nommée t dans f int i = 3; f<int>(i); // ok: création d'uen varaible nommée t dans f qui est la // copie de i g<int>(2); // ok: création d'une variable (constante) nommée t dans g g<int>(i); // idem h<int>(2); // ok: création d'une variable temporaire non nommée // qui sera passée par référence constante sous le nom de t h<int>(i); // ok: passage d'une variable existante sous la forme // d'une référence constante i<int>(2); // refusé: seule les références constantes peuvent accepter // les variables temporaires non nommées i<int>(i); // par contre, on peut passer n'importe quelle variable // existante sous la forme d'une référence }
Lorsque tu décide d'écrire ta fonction, la logique voudrait que tu aie déjà réfléchi à l'algorithme que tu va mettre en oeuvre au sein de cette fonction:
Tu *devrais* en effet avoir une idée aussi précise que possible des différentes étapes qui seront suivies par ta fonction afin de lui permettre de présenter le comportement souhaité...
Tu *devrais* donc normalement être en mesure de déterminer si l'objet qui sera passé en paramètre sera modifié ou non ;)
Or nous savons que nous ne risquons rien à faire passer "temporairement" (comprend: le temps que dure la fonction appelée) un objet qui n'est pas constant à la base pour un objet constant: tout ce que nous faisons, c'est restreindre les différentes interactions que nous pouvons envisager sur cet objet ;)
D'un autre coté, nous savons aussi que, si nous décidons de permettre la modification de l'objet, il faut que notre objet soit à la base déclaré comme non constant: si l'objet est déclaré à la base constant, nous n'en attendons pas moins de la part du compilateur que le fait de nous signaler que nous allons tenter de modifier un objet qui ne peut normalement pas l'être ;)
C'est la raison pour laquelle le principe de const correctness t'incite, chaque fois qu'une fonction ne doit pas modifier un objet passé en argument, à indiquer à cette fonction de considérer "temporairement" (comprend: le temps que dure l'exécution de la fonction) l'argument passé "comme s'il était destiné à ne pas être modifié"...