Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Résolution de fonction dans une fonction template ...

7 réponses
Avatar
Pierre Barbier de Reuille
Bonjour,

voil=E0, je me pose une question sur la validit=E9 de ce code l=E0:

=3D=3D=3D8<=3D=3D=3D=3D=3D8<=3D=3D=3D=3D=3D8<=3D=3D=3D=3D=3D8<=3D=3D=3D=3D=
=3D8<=3D=3D=3D=3D=3D8<=3D=3D=3D=3D=3D8<=3D=3D

#include <iostream>

using namespace std;

template <typename C>
void call_tmpl(C i)
{
my_fct(i);
}

int main()
{
call_tmpl(3);
return 0;
}

void my_fct(int a)
{
cout << "my_fct<int> =3D " << a << endl;
}

=3D=3D=3D8<=3D=3D=3D=3D=3D8<=3D=3D=3D=3D=3D8<=3D=3D=3D=3D=3D8<=3D=3D=3D=3D=
=3D8<=3D=3D=3D=3D=3D8<=3D=3D

Alors il se trouve qu'il compile avec g++ 4. Par contre, je trouve
=E9trange que la fonction my_fct(int) soit trouv=E9e !!
J'avoue que je me serais attendu =E0 ce que =E7a =E9choue ! Par contre,
d=E9finir "myfct(int)" *avant* main devrait fonctionner, puisque si j'ai
bien compris, la r=E9solution des noms se fait en utilisant le contexte
d'appel du template.

Merci d'avance,

Pierre

7 réponses

Avatar
Alp Mestan
Pierre Barbier de Reuille wrote:
Bonjour,

voilà, je me pose une question sur la validité de ce code là:

===8<=====8<=====8<=====8<=====8<=====8<=====8<= >
#include <iostream>

using namespace std;

template <typename C>
void call_tmpl(C i)
{
my_fct(i);
}

int main()
{
call_tmpl(3);
return 0;
}

void my_fct(int a)
{
cout << "my_fct<int> = " << a << endl;
}

===8<=====8<=====8<=====8<=====8<=====8<= >
Alors il se trouve qu'il compile avec g++ 4. Par contre, je trouve
étrange que la fonction my_fct(int) soit trouvée !!
J'avoue que je me serais attendu à ce que ça échoue ! Par contre,
définir "myfct(int)" *avant* main devrait fonctionner, puisque si j'ai
bien compris, la résolution des noms se fait en utilisant le contexte
d'appel du template.

Merci d'avance,

Pierre


Etant donné que ta fonction call_tmpl est template, le compilo génère
une fonction pour chaque type avec lequel tu l'appelles.
Et probablement que la fonction générée est générée après my_fct(int),
d'où le fait qu'elle soit déjà définie.

Sur d'autres compilo, tu as essayé ? Si ils génèrent tous les fonctions
à la fin, ça marchera sur tous.

Cependant, mis à part la curiosité, il n'y a aucun problème avec ce code
si il ne marche pas avec un compilo, puisqu'il suffit de déclarer
my_fct(int) avant, en effet.

Avatar
Cyrille

Sur d'autres compilo, tu as essayé ? Si ils génèrent tous les fonctions
à la fin, ça marchera sur tous.


Essayé avec Visual C++ 2005, il a le même comportement que g++ 4.
Avec Comeau (http://www.comeaucomputing.com/tryitout/) ça échoue, et il
faut placer le my_fct avant le call_tmpl pour que le code compile.
Simplement avant le main ça ne marche pas.

--
Cyrille

Avatar
Alp Mestan
Cyrille wrote:
Sur d'autres compilo, tu as essayé ? Si ils génèrent tous les fonctions
à la fin, ça marchera sur tous.


Essayé avec Visual C++ 2005, il a le même comportement que g++ 4.
Avec Comeau (http://www.comeaucomputing.com/tryitout/) ça échoue, et il
faut placer le my_fct avant le call_tmpl pour que le code compile.
Simplement avant le main ça ne marche pas.



Comeau doit probablement faire le name lookup de manière plus stricte.
Peut-être qu'il écrit toutes les versions de la fonction template à
l'endroit où est définie la fonction template, ce qui entraine ici qu'il
ne connaîtra pas my_fct(int) au moment où il aura généré call_tmpl<int>.

De toute manière la bonne démarche dans tous les cas est de déclarer
my_fct avant call_tmpl, quitte à la redéfinir plus bas si l'on veut, par
contre.


Avatar
James Kanze
On Jul 21, 7:09 am, Pierre Barbier de Reuille
wrote:

voilà, je me pose une question sur la validité de ce code là:

===8<=====8<=====8<=====8<=== ==8<=====8<=====8<==

#include <iostream>

using namespace std;

template <typename C>
void call_tmpl(C i)
{
my_fct(i);
}

int main()
{
call_tmpl(3);
return 0;
}

void my_fct(int a)
{
cout << "my_fct<int> = " << a << endl;
}

===8<=====8<=====8<=====8<=== ==8<=====8<==

Alors il se trouve qu'il compile avec g++ 4. Par contre, je trouve
étrange que la fonction my_fct(int) soit trouvée !!


En effet. Selon §14.6.4.1/1 :

For a function template specialization, a member
function template specialization, or a specialization
for a member function or static data member of a class
template, if the specialization is implicitly
instantiated because it is referenced from within
another template specialization and the context from
which it is referenced depends on a template parameter,
the point of instantiation of the specialization is the
point of instantiation of the enclosing specialization.
Otherwise, the point of instantiation for such a
specialization immediately follows the namespace scope
declaration or definition that refers to the
specialization.

En somme, le comportement doit être exactement le même que si tu
définissais une fonction non-template immédiatement après main()
(et avant my_fct).

J'avoue que je me serais attendu à ce que ça échoue !


Dans ce cas précis, la norme dit qu'il doit échouer, et exige un
diagnostique. Plus généralement, en revanche, c'est le genre de
chose à laquelle il faut faire beaucoup d'attention toi-même,
pour éviter un comportement indéfini. Tout à la fin de la
section pré-citée (paragraphe 7), il dit :

A specialization for a function template, a member
function template, or of a member function or static
data member of a class template may have multiple points
of instantiations within a translation unit. A
specialization for a class template has at most one
point of instantiation within a translation unit. A
specialization for any template may have points of
instantiation in multiple translation units. If two
different points of instantiation give a template
specialization different meanings according to the one
definition rule (3.2), the program is ill-formed, no
diagnostic required.

C-à-d que dans des cas plus réaliste (où le template sert
plusieurs fois, etc.), la risque d'un comportement indéfini est
fort.

Par contre,
définir "myfct(int)" *avant* main devrait fonctionner, puisque si j'ai
bien compris, la résolution des noms se fait en utilisant le contexte
d'appel du template.


Voir le premier paragraphe que j'ai cité. La résolution ne peut
pas se faire exactement dans le contexte d'appel ; dans le
contexte de l'appel, par exemple, des variables locale de la
fonction appelante sont visible. Grosso modo, pour un template
de fonction, la contexte est immédiatement après, et pour un
template de classe, immédiatement avant. La rationale de cette
différence est, je crois, que dans beaucoup d'utilisations d'un
type, il faut un type complet (donc, que le template de classe
soit instantié *avant* l'utilisation), mais qu'on ne veut pas
interdire la récursion (donc, que la declaration, au moins, de
la fonction appelante soit visible au point d'instantiation).

Aussi, évidemment, tout ça ne vaut que pour les noms dépendants.
Les noms non-dépendants sont cherchés lors de la définition du
template, et non lors de l'instantiation.

--
James Kanze (Gabi Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Pierre Barbier de Reuille
Ok, merci beaucoup à tous ! Même si les réponses n'arrangent pas mon
affaire ;)

-- Pierre
Avatar
Alp Mestan
Pierre Barbier de Reuille wrote:
Ok, merci beaucoup à tous ! Même si les réponses n'arrangent pas mon
affaire ;)

-- Pierre



Selon le cas, il te suffit :
- soit d'inclure le header où est déclaré ta fonction avant call_tmpl
- soit de déclarer ta fonction avant call_tmpl

Ca ne te convient pas ?

Avatar
Pierre Barbier de Reuille
On 23 juil, 15:55, Alp Mestan wrote:
Pierre Barbier de Reuille wrote:

Ok, merci beaucoup à tous ! Même si les réponses n'arrangent pas mon
affaire ;)

-- Pierre


Selon le cas, il te suffit :
- soit d'inclure le header où est déclaré ta fonction avant call_tm pl
- soit de déclarer ta fonction avant call_tmpl

Ca ne te convient pas ?


En fait, non ça ne me convient pas ... car je souhaite éviter
d'imposer à l'utilisateur de prédéfinir classe et/ou fonction.
Bon, si ça intéresse quelqu'un, au final il se trouve que ma fonction
template principale (call_tmpl) a en argument un objet que
l'utilisateur définit entièrement. Donc le plus simple est de demander
à ce que cet objet ait des méthodes particulières.

L'exemple deviendrait:

==8<===8<===8<===8<===8<===8<===8< ===8<=

#include <iostream>

using namespace std;

template <typename C, typename T>
void call_tmpl(C i, T& t)
{
t.my_fct(i);
}

class UserClass
{
public:
void my_fct(int a)
{
cout << "my_fct<int> = " << a << endl;
}
};

int main()
{
UserClass u;
call_tmpl(3,u);
return 0;
}

=>8===>8===>8===>8===>8===>8===>8= ==>8===>8==

Voilà, je pense que ça devrait marcher sans problème.

Encore merci à tous,

Pierre