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

Conversion implicite et template

11 réponses
Avatar
Marc Boyer
J'ai un problème de code qui marche sans template et ne marche pas avec.
C'est un peu long (désolé).

Je tente d'écrire une surcouche à int, Entier, avec conversion
implicite de int->Entier (constructeur), et une surcharge
de l'opérateur binaire +.

Ca marche.

Et si je fait de Entier un template, ça refuse de compiler...
> no match for `Entier<int>& + int' operator

Une idée ?

Merci d'avance.

Marc Boyer
------------- Le code qui marche ---------------
class Entier {
private:
int val;
public:
Entier(int v):val(v){}
Entier& operator+=(const Entier& inc){
val+=inc.val;
return *this;
}
};

Entier operator+(const Entier& left, const Entier& right){
Entier tmp(left);
tmp+= right;
return tmp;
}

int main(){
Entier a=1;
Entier b=1;
b = a + 1;
b = 1 + a;
return 0;
}

------------------------ Le code qui plante -------------------
te <typename T>
class Entier {
private:
T val;
public:
Entier(T v):val(v){}
Entier<T>& operator+=(const Entier<T>& inc){
val+=inc.val;
return *this;
}
};

template <typename T>
Entier<T> operator+(const Entier<T>& left, const Entier<T>& right){
Entier<T> tmp(left);
tmp+= right;
return tmp;
}

int main(){
Entier<int> a=1;
Entier<int> b=1;
b = a + 1;
b = 1 + a;
return 0;
}





--
Lying for having sex or lying for making war? Trust US presidents :-(

10 réponses

1 2
Avatar
Gabriel Dos Reis
Marc Boyer writes:

[...]

| Et si je fait de Entier un template, ça refuse de compiler...
| > no match for `Entier<int>& + int' operator
|
| Une idée ?

Classique. Dans le processus de déduction d'arguments de templates,
très peu de conversion sont tolérées (et les rares conversion tolérées
ne sont tentées qu'en dernier recours). Étant donné

| template <typename T>
| Entier<T> operator+(const Entier<T>& left, const Entier<T>& right){
| Entier<T> tmp(left);
| tmp+= right;
| return tmp;
| }

et "a + 1" où a est de type Entier<int>, le compilateur va essayer de
déduire une valeur de T pour que les types des opérandes "a" et "1" de
operator+ soient "compatibles" avec Entier<int>. Pour "a", la
déduction réssit avec T = int. Pour "1", l'équation n'a pas de
solution. Donc le processus échoue et la fonction operator+ n'est pas
considérée, d'où le message du compilateur.
(c'est un peu raccourci, l'essentiel est là).

-- Gaby
Avatar
Marc Boyer
Gabriel Dos Reis wrote:
Marc Boyer writes:
et "a + 1" où a est de type Entier<int>, le compilateur va essayer de
déduire une valeur de T pour que les types des opérandes "a" et "1" de
operator+ soient "compatibles" avec Entier<int>. Pour "a", la
déduction réssit avec T = int. Pour "1", l'équation n'a pas de
solution. Donc le processus échoue et la fonction operator+ n'est pas
considérée, d'où le message du compilateur.
(c'est un peu raccourci, l'essentiel est là).


Donc, la solution, c'est de faire en plus des operateurs
Entier<T> operator+(const T left, const Entier<T> right);
Entier<T> operator+(const Entier<TW left, const T right);
C'est ça ?

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(

Avatar
Gabriel Dos Reis
Marc Boyer writes:
| Donc, la solution, c'est de faire en plus des operateurs
| Entier<T> operator+(const T left, const Entier<T> right);
| Entier<T> operator+(const Entier<TW left, const T right);
| C'est ça ?

Oui, c'est -une- solution.

-- Gaby
Avatar
Marc Boyer
Gabriel Dos Reis wrote:
Marc Boyer writes:
| Donc, la solution, c'est de faire en plus des operateurs
| Entier<T> operator+(const T left, const Entier<T> right);
| Entier<T> operator+(const Entier<TW left, const T right);
| C'est ça ?

Oui, c'est -une- solution.


Et les autres solutions sont d'une complexité raisonnable ?
Bon, parce que là, ça me fait 2 copier/coller par opérateur
binaire. C'est lourd mais supportable.

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(

Avatar
Gabriel Dos Reis
Marc Boyer writes:

| Gabriel Dos Reis wrote:
| > Marc Boyer writes:
| >| Donc, la solution, c'est de faire en plus des operateurs
| >| Entier<T> operator+(const T left, const Entier<T> right);
| >| Entier<T> operator+(const Entier<TW left, const T right);
| >| C'est ça ?
| >
| > Oui, c'est -une- solution.
|
| Et les autres solutions sont d'une complexité raisonnable ?

idiome de la classe de base paramétrée par les classe dérivées qui
génère automatiquement des opérateurs qui vont bien.


template<typename T>
struct ArithmeticType {
friend T operator+(T a, T b)
{
return add(a, b);
}

// ainsi de suite ...
};

template<typename T>
struct Entier : ArithmeticType<Entier<T> > {
// ...
friend Entier<T> add(Entier<T> a, Entier<T> b)
{
// ...
}
};

Ici, il n'y a pas déduction d'argument template, donc les conversions
usuelles s'appliquent.

-- Gaby
Avatar
Marc Boyer
Gabriel Dos Reis wrote:
Marc Boyer writes:

idiome de la classe de base paramétrée par les classe dérivées qui
génère automatiquement des opérateurs qui vont bien.


Aurais-tu le nom anglais de l'idiome, que je fasse des
recherches avec Google ? Voire carrément des pointeurs ?

template<typename T>
struct ArithmeticType {
friend T operator+(T a, T b)
{
return add(a, b);
}

// ainsi de suite ...
};

template<typename T>
struct Entier : ArithmeticType<Entier<T> > {
// ...
friend Entier<T> add(Entier<T> a, Entier<T> b)
{
// ...
}
};

Ici, il n'y a pas déduction d'argument template, donc les conversions
usuelles s'appliquent.


J'ai lu et relu ton message. Je ne comprends pas pourquoi
il n'y a plus de déduction d'argument template.
Entier<int> a;
a = a + 1;
pour renvoyer le + vers le add, il faut quand même qu'il
déduise que T vaut Entier<int>, et qu'il faut convertir
le int en Entier<int>, non ? Où mon raisonnement
cloche-t-il ?

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(

Avatar
Gabriel Dos Reis
Marc Boyer writes:

| Gabriel Dos Reis wrote:
| > Marc Boyer writes:
| >
| > idiome de la classe de base paramétrée par les classe dérivées qui
| > génère automatiquement des opérateurs qui vont bien.
|
| Aurais-tu le nom anglais de l'idiome, que je fasse des
| recherches avec Google ? Voire carrément des pointeurs ?

si tu utilises google, cherche apres Barton&Nackman -- ou regarde
carrément dans leur bouquin -- ou après Coplien et "Curiously Recurring
Template Pattern".

| > template<typename T>
| > struct ArithmeticType {
| > friend T operator+(T a, T b)
| > {
| > return add(a, b);
| > }
| >
| > // ainsi de suite ...
| > };
| >
| > template<typename T>
| > struct Entier : ArithmeticType<Entier<T> > {
| > // ...
| > friend Entier<T> add(Entier<T> a, Entier<T> b)
| > {
| > // ...
| > }
| > };
| >
| > Ici, il n'y a pas déduction d'argument template, donc les conversions
| > usuelles s'appliquent.
|
| J'ai lu et relu ton message. Je ne comprends pas pourquoi
| il n'y a plus de déduction d'argument template.

C'est parce que, dns ce cas, pour qu'il y ait déduction d'argument de
templates, il faut qu'il y a une fonction template. Or il n'y en a pas :-).

La fonction amie operator+ (définie dans ArithmeticType<>) et la
fonction amie add (définie dans Entier<>) ne sont pas des fonctions
templates.

| Entier<int> a;
| a = a + 1;
| pour renvoyer le + vers le add, il faut quand même qu'il
| déduise que T vaut Entier<int>, et qu'il faut convertir
| le int en Entier<int>, non ? Où mon raisonnement
| cloche-t-il ?

De "a + 1", le compilateur déduit que tu fais appel à une fonction
nommée operator+. Pour voir laquelle, il considère les operateurs
câblés ansi que toute fonction nommée operator+ que la recherche de
nom va lui donner. En particuler la recherche de nom dépendant des
arguments (ou Koenig lookup pour faire court) considère Entier<int>
ainsi que sa class de base ArithmeticType<Entier<int> > où elle trouve
un operator+. Comme ce n'est pas une fonction template, on ne passe
pas par la case déduction d'argument de template. On va directement à
la phase résolution de surcharge. Et là tout se déroule comme dans le
cas où tu n'avais pas templaté ta classe.

Dans operator+, on fait de nouveau une recherche de nom dépendant des
arguments et on trouve add() definie dans Entier<int>.

-- Gaby
Avatar
Patrick Mézard
Par pure curiosité, tu as regardé :
http://www.boost.org/libs/utility/operators.htm ?
Vu le code que tu postes depuis un moment, ça pourrait pas mal t'aider non ?

Patrick Mézard
Avatar
Marc Boyer
Gabriel Dos Reis wrote:
Marc Boyer writes:

| Gabriel Dos Reis wrote:
| J'ai lu et relu ton message. Je ne comprends pas pourquoi
| il n'y a plus de déduction d'argument template.

C'est parce que, dns ce cas, pour qu'il y ait déduction d'argument de
templates, il faut qu'il y a une fonction template. Or il n'y en a pas :-).

La fonction amie operator+ (définie dans ArithmeticType<>) et la
fonction amie add (définie dans Entier<>) ne sont pas des fonctions
templates.


La distinction qui m'avait échappée, c'est qu'une fonction
template n'est pas la même chose qu'une function membre d'une
classe template (ce dont j'aurais pu me souvenir puisque l'une
accepte les spécialisations partielles et pas l'autre), et
que les fonctions amies de classes templates sont encore
une troisième chose, c'est ça ?

De "a + 1", le compilateur déduit que tu fais appel à une fonction
nommée operator+. Pour voir laquelle, il considère les operateurs
câblés ansi que toute fonction nommée operator+ que la recherche de
nom va lui donner. En particuler la recherche de nom dépendant des
arguments (ou Koenig lookup pour faire court) considère Entier<int>
ainsi que sa class de base ArithmeticType<Entier<int> > où elle trouve
un operator+.


Mais cet operator+ est une fonction amie d'une classe template,
pas une fonction membre de cette classe ?
C'est à dire que le "Koening lookup" considèrent différement
les fonctions templates et les fonctions amies de classes
template, c'est ça ?

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(

Avatar
Gabriel Dos Reis
Marc Boyer writes:

| Gabriel Dos Reis wrote:
| > Marc Boyer writes:
| >
| >| Gabriel Dos Reis wrote:
| >| J'ai lu et relu ton message. Je ne comprends pas pourquoi
| >| il n'y a plus de déduction d'argument template.
| >
| > C'est parce que, dns ce cas, pour qu'il y ait déduction d'argument de
| > templates, il faut qu'il y a une fonction template. Or il n'y en a pas :-).
| >
| > La fonction amie operator+ (définie dans ArithmeticType<>) et la
| > fonction amie add (définie dans Entier<>) ne sont pas des fonctions
| > templates.
|
| La distinction qui m'avait échappée, c'est qu'une fonction
| template n'est pas la même chose qu'une function membre d'une
| classe template (ce dont j'aurais pu me souvenir puisque l'une
| accepte les spécialisations partielles et pas l'autre), et
| que les fonctions amies de classes templates sont encore
| une troisième chose, c'est ça ?

Presque. Les fonctions amies d'une classe template ne sont pas
forcément templates. Elles ne sont pas templates juste parce qu'elles
sont définies dans une class template. Elles ne sont templates que si
leurs déclarations sont précédées de l'entête « template<...> ».

| > De "a + 1", le compilateur déduit que tu fais appel à une fonction
| > nommée operator+. Pour voir laquelle, il considère les operateurs
| > câblés ansi que toute fonction nommée operator+ que la recherche de
| > nom va lui donner. En particuler la recherche de nom dépendant des
| > arguments (ou Koenig lookup pour faire court) considère Entier<int>
| > ainsi que sa class de base ArithmeticType<Entier<int> > où elle trouve
| > un operator+.
|
| Mais cet operator+ est une fonction amie d'une classe template,
| pas une fonction membre de cette classe ?

C'est exact.

| C'est à dire que le "Koening lookup" considèrent différement
| les fonctions templates et les fonctions amies de classes
| template, c'est ça ?

Non. Une recherche de nom ne fait que chercher des déclarations
associées à un nom -- ceci de manière uniforme. La version Koenig
lookup est une recherche de nom qui examine, en plus, ce que l'on
appelle les classes et namespace associés des arguments.

-- Gaby
1 2